class Color32 {
    constructor(r, g, b, a = 255) {
        this.values = new Uint8ClampedArray(4);
        this.values[0] = r;
        this.values[1] = g;
        this.values[2] = b;
        this.values[3] = a;
    }
    get R() {
        return this.values[0];
    }
    get G() {
        return this.values[1];
    }
    get B() {
        return this.values[2];
    }
    get A() {
        return this.values[3];
    }
    set R(v) {
        this.values[0] = v;
    }
    set G(v) {
        this.values[1] = v;
    }
    set B(v) {
        this.values[2] = v;
    }
    set A(v) {
        this.values[3] = v;
    }
    static Equal(c, c2) {
        return (c.R == c2.R && c.G == c2.G && c.B == c2.B && c.A == c2.A);
    }
    toString() {
        return `rgba(${this.R},${this.G},${this.B},${this.A})`;
    }
    Clone() {
        return new Color32(this.R, this.G, this.B, this.A);
    }
}

class BaseElement {
    constructor() {
        this.elems = [];
    }
    Show() {
        this._root.style.visibility = "visible";
    }
    Hide() {
        this._root.style.visibility = "hidden";
    }
    getValue() {
        throw new Error("no value.");
    }
    getRoot() {
        return this._root;
    }
    setBackColor(color) {
        this._root.style.backgroundColor = color.toString();
    }
    AddChild(elem) {
        this._root.appendChild(elem.getRoot());
        this.elems.push(elem);
    }
    RemoveAllChild() {
        for (var i = 0; i < this.elems.length; i++) {
            this.elems[i].getRoot().remove();
        }
        this.elems.splice(0);
    }
    RemoveChild(elem) {
        let i = this.elems.indexOf(elem);
        if (i < 0)
            return;
        this.elems.splice(i, 1);
        elem.getRoot().remove();
    }
    RemoveChildAt(index) {
        let elem = this.elems.splice(index, 1)[0];
        elem.getRoot().remove();
    }
    GetChildCount() {
        return this.elems.length;
    }
    GetChild(index) {
        return this.elems[index];
    }
    Style_Fill() {
        this._root.style.position = "absolute";
        this._root.style.left = "0px";
        this._root.style.top = "0px";
        this._root.style.right = "0px";
        this._root.style.bottom = "0px";
        this._root.style.width = "100%";
        this._root.style.height = "100%";
    }
    Style_Size(width, height) {
        this._root.style.width = width + "px";
        this._root.style.height = height + "px";
    }
    Style_Fix(x, y, width, height) {
        this._root.style.position = "absolute";
        this._root.style.left = x + "px";
        this._root.style.top = y + "px";
        this._root.style.right = "auto";
        this._root.style.bottom = "auto";
        this._root.style.width = width + "px";
        this._root.style.height = height + "px";
    }
}
class Screen extends BaseElement {
    constructor() {
        super();
        this._root = document.body;
    }
    getRoot() {
        throw new Error("no root.");
    }
}
class Label extends BaseElement {
    constructor(text = "") {
        super();
        let div = this._root = document.createElement("div");
        this.span = document.createElement("span");
        this.span.textContent = text;
        div.appendChild(this.span);
    }
    getValue() {
        return this.span.innerText;
    }
    getText() {
        return this.span.innerText;
    }
    SetText(text) {
        this.span.innerText = text;
    }
}
class LabelButton extends BaseElement {
    constructor(text = "") {
        super();
        this.colorBack = new Color32(0, 0, 0, 0);
        this.colorBackDown = new Color32(255, 255, 0);
        this.colorLabel = new Color32(0, 0, 0, 255);
        this.colorLabelDown = new Color32(0, 0, 255, 255);
        let div = this._root = document.createElement("div");
        this.span = document.createElement("span");
        this.span.textContent = text;
        div.appendChild(this.span);
        //this._root.style.position = "absolute";
        div.style.width = "100%";
        div.onmousedown = (e) => {
            div.style.backgroundColor = this.colorBackDown.toString();
            this.span.style.color = this.colorLabelDown.toString();
        };
        div.onmouseup = (e) => {
            div.style.backgroundColor = this.colorBack.toString();
            this.span.style.color = this.colorLabel.toString();
            if (this.onClick != null) {
                this.onClick();
            }
        };
        div.onmouseleave = (e) => {
            div.style.backgroundColor = this.colorBack.toString();
            this.span.style.color = this.colorLabel.toString();
        };
        this.UpdateColor();
    }
    UpdateColor() {
        let div = this._root;
        div.style.backgroundColor = this.colorBack.toString();
        this.span.style.color = this.colorLabel.toString();
    }
    getValue() {
        return this.span.innerText;
    }
    getText() {
        return this.span.innerText;
    }
    SetText(text) {
        this.span.innerText = text;
    }
}
class TextBox extends BaseElement {
    constructor(title, defvalue) {
        super();
        let div = this._root = document.createElement("div");
        this.span = document.createElement("span");
        this.span.textContent = title;
        div.appendChild(this.span);
        this.input = document.createElement("input");
        this.input.value = defvalue;
        this.input.type = "input";
        div.appendChild(this.input);
    }
    setTitle(title) {
        this.span.textContent = title;
    }
    setValue(text) {
        this.input.value = text;
    }
    getValue() {
        return this.input.value;
    }
}
class Toggle extends BaseElement {
    constructor(title, defvalue = false) {
        super();
        let div = this._root = document.createElement("div");
        this.span = document.createElement("span");
        this.span.textContent = title;
        div.appendChild(this.span);
        this.input = document.createElement("input");
        this.input.checked = defvalue;
        this.input.type = "checkbox";
        div.appendChild(this.input);
    }
    setTitle(title) {
        this.span.textContent = title;
    }
    setValue(value) {
        this.input.checked = value;
    }
    getValue() {
        return this.input.checked;
    }
}
class Button extends BaseElement {
    constructor(title, click = null) {
        super();
        let btn = this._root = document.createElement("button");
        btn.innerText = title;
        btn.onclick = click;
    }
    SetText(value) {
        this._root.textContent = value;
    }
    SetClickEvent(click) {
        this._root.onclick = click;
    }
}
class Space extends BaseElement {
    constructor(width = 32) {
        super();
        this._root = document.createElement("span");
        this._root.style.width = width + "px";
        this._root.style.display = "inline-flex";
    }
}
class Panel extends BaseElement {
    constructor() {
        super();
        let div = this._root = document.createElement("div");
        //div.style.position = "absolute";
        div.style.border = "1px solid";
        div.style.borderColor = "#fff";
        div.style.color = "#fff";
        div.style.left = "0px";
        div.style.right = "0px";
        div.style.top = "0px";
        div.style.bottom = "0px";
    }
    SetBorder(width) {
        let div = this._root;
        div.style.border = width + "px solid";
    }
    SetVCenter() {
        let div = this._root;
        div.style.top = "50%";
        div.style.position = "relative";
        div.style.transform = "translateY(-50%)";
    }
    UseScrollV() {
        let div = this._root;
        div.style.overflowY = "scroll";
    }
    UseScrollH() {
        let div = this._root;
        div.style.overflowX = "scroll";
    }
}

class Splitter extends Panel {
    constructor() {
        super();
        this.SetBorder(0);
        this._root.style.position = "absolute";
        this._panel1 = new Panel();
        this._panel1.SetBorder(0);
        this._root.appendChild(this._panel1.getRoot());
        this._panel1.getRoot().style.backgroundColor = "#c33";
        this._panel1.getRoot().style.position = "absolute";
        this._panel1.getRoot().style.left = "0px";
        this._panel1.getRoot().style.top = "0px";
        this._panel1.getRoot().style.bottom = "0px";
        this._panel1.getRoot().style.width = "100px";
        this._panel2 = new Panel();
        this._panel2.SetBorder(0);
        this._root.appendChild(this._panel2.getRoot());
        this._panel2.getRoot().style.backgroundColor = "#33c";
        this._panel2.getRoot().style.position = "absolute";
        this._panel2.getRoot().style.left = "100px";
        this._panel2.getRoot().style.top = "0px";
        this._panel2.getRoot().style.bottom = "0px";
        this._panel2.getRoot().style.right = "0px";
    }
    SetSplitPosLeft(pos) {
        this._panel1.getRoot().style.left = "0px";
        this._panel1.getRoot().style.right = "auto";
        this._panel1.getRoot().style.top = "0px";
        this._panel1.getRoot().style.bottom = "0px";
        this._panel1.getRoot().style.width = pos + "px";
        this._panel1.getRoot().style.height = "auto";
        this._panel2.getRoot().style.left = pos + "px";
        this._panel2.getRoot().style.top = "0px";
        this._panel2.getRoot().style.bottom = "0px";
        this._panel2.getRoot().style.right = "0px";
        this._panel2.getRoot().style.width = "auto";
        this._panel2.getRoot().style.height = "auto";
    }
    SetSplitPosRight(pos) {
        this._panel1.getRoot().style.left = "0px";
        this._panel1.getRoot().style.top = "0px";
        this._panel1.getRoot().style.bottom = "0px";
        this._panel1.getRoot().style.right = pos + "px";
        this._panel1.getRoot().style.width = "auto";
        this._panel1.getRoot().style.height = "auto";
        this._panel2.getRoot().style.top = "0px";
        this._panel2.getRoot().style.bottom = "0px";
        this._panel2.getRoot().style.left = "auto";
        this._panel2.getRoot().style.right = "0px";
        this._panel2.getRoot().style.width = pos + "px";
        this._panel1.getRoot().style.height = "auto";
    }
    SetSplitPosTop(pos) {
        this._panel1.getRoot().style.left = "0px";
        this._panel1.getRoot().style.bottom = "auto";
        this._panel1.getRoot().style.top = "0px";
        this._panel1.getRoot().style.right = "0px";
        this._panel1.getRoot().style.height = pos + "px";
        this._panel1.getRoot().style.width = "auto";
        this._panel2.getRoot().style.top = pos + "px";
        this._panel2.getRoot().style.left = "0px";
        this._panel2.getRoot().style.bottom = "0px";
        this._panel2.getRoot().style.right = "0px";
        this._panel2.getRoot().style.width = "auto";
        this._panel2.getRoot().style.height = "auto";
    }
    SetSplitPosBottom(pos) {
        this._panel1.getRoot().style.left = "0px";
        this._panel1.getRoot().style.bottom = pos + "px";
        this._panel1.getRoot().style.top = "0px";
        this._panel1.getRoot().style.right = "0px";
        this._panel1.getRoot().style.height = "auto";
        this._panel1.getRoot().style.width = "auto";
        this._panel2.getRoot().style.top = "auto";
        this._panel2.getRoot().style.left = "0px";
        this._panel2.getRoot().style.bottom = "0px";
        this._panel2.getRoot().style.right = "0px";
        this._panel2.getRoot().style.width = "auto";
        this._panel2.getRoot().style.height = pos + "px";
    }
    AddChild(elem) {
        throw new Error("use left or right");
    }
}

class Group extends Panel {
    constructor() {
        super();
        this._root.style.position = "absolute";
        this._label = new Label("group");
        this._root.appendChild(this._label.getRoot());
        this._label.getRoot().style.position = "absolute";
        this._label.getRoot().style.left = "0px";
        this._label.getRoot().style.right = "0px";
        this._label.getRoot().style.top = "0px";
        this._label.getRoot().style.height = "20px";
        this._panel = new Panel();
        this._root.appendChild(this._panel.getRoot());
        this._panel.getRoot().style.position = "absolute";
        this._panel.getRoot().style.left = "0px";
        this._panel.getRoot().style.right = "0px";
        this._panel.getRoot().style.top = "20px";
        this._panel.getRoot().style.bottom = "0px";
    }
    SetTitle(title) {
        this._label.SetText(title);
    }
    AddChild(elem) {
        throw new Error("use panel");
    }
}

class CanvasRaw extends BaseElement {
    constructor() {
        super();
        this._root = document.createElement("canvas");
        this._root.style.left = "0px";
        this._root.style.right = "0px";
        this._root.style.top = "0px";
        this._root.style.bottom = "0px";
        this._root.style.position = "absolute";
        this._root.style.width = "100%";
        this._root.style.height = "100%";
    }
    getHtml() {
        return this._root;
    }
}

var __awaiter$a = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
class TTPathTool {
    static GetFileName(filename) {
        if (filename.includes("/")) {
            let fi = filename.lastIndexOf("/");
            let shortname = filename.substring(fi + 1);
            return shortname;
        }
        if (filename.includes("\\")) {
            let fi = filename.lastIndexOf("\\");
            let shortname = filename.substring(fi + 1);
            return shortname;
        }
        return filename;
    }
    static GetPathName(filename) {
        if (filename.includes("/")) {
            let fi = filename.lastIndexOf("/");
            if (fi < 0)
                return filename;
            let shortname = filename.substring(0, fi);
            return shortname;
        }
        if (filename.includes("\\")) {
            let fi = filename.lastIndexOf("\\");
            let shortname = filename.substring(0, fi);
            return shortname;
        }
        return "";
    }
    static GetFileNameWithoutExt(filename) {
        let shortname = this.GetFileName(filename);
        let di = shortname.indexOf(".");
        if (di < 0)
            return shortname;
        return shortname.substring(0, di);
    }
    static GetExt(filename) {
        let shortname = this.GetFileName(filename);
        console.log("_getext:" + shortname);
        let di = shortname.indexOf(".");
        if (di < 0)
            return "";
        return shortname.substring(di);
    }
}
class IO {
    static GetPluginUrl(suburl) {
        let subl = suburl;
        if (suburl[0] == "/")
            subl = suburl.substring(1);
        else
            subl = suburl;
        return this.baseurl + subl;
    }
    static Init() {
        window.onmessage = (msg) => {
            if (msg.data["cmd"] != undefined) {
                //console.log("recv:" + JSON.stringify(msg));//.data["cmd"];
                this.dataqueue.push(msg.data);
            }
        };
        this.api = acquireVsCodeApi();
        this.baseurl = getVsCodePluginPath();
        if (this.baseurl.endsWith("/") == false) {
            this.baseurl += "/";
        }
        this.editfile = getVsCodeEditFileName(); //得到编辑的文件名
    }
    static GetEditFileName() {
        return this.editfile;
    }
    static GetEditBaseName() {
        return TTPathTool.GetFileNameWithoutExt(this.editfile);
    }
    static Hex2Str(data) {
        let str = "";
        for (var i = 0; i < data.length; i++) {
            let h = data[i].toString(16);
            if (h.length == 1) {
                str += ("0" + h);
            }
            else {
                str += h;
            }
        }
        return str;
    }
    static Str2Hex(text) {
        let u8 = new Uint8Array(text.length / 2);
        for (var i = 0; i < u8.length; i++) {
            let h = text.substring(i * 2, i * 2 + 2);
            u8[i] = parseInt(h, 16);
        }
        return u8;
    }
    static getBaseUrl() {
        return this.baseurl;
    }
    static getEditFile() {
        return this.editfile;
    }
    static sleep(ms) {
        return __awaiter$a(this, void 0, void 0, function* () {
            return new Promise(resolve => setTimeout(resolve, ms));
        });
    }
    static LoadEditFile_AsText() {
        return __awaiter$a(this, void 0, void 0, function* () {
            return yield this.LoadTextFile(this.editfile);
        });
    }
    static FindFile(path, depth = 1) {
        return __awaiter$a(this, void 0, void 0, function* () {
            let sname = path;
            while (sname.includes("\\")) {
                sname = sname.replace("\\", "/");
            }
            this.api.postMessage({ "cmd": "fs_find", "data": { "depth": depth, "path": path } });
            while (true) {
                yield this.sleep(1);
                for (var i = 0; i < this.dataqueue.length; i++) {
                    let q = this.dataqueue[i];
                    if (q["cmd"] == "filelist") {
                        let fname = q["path"];
                        while (fname.includes("\\")) {
                            fname = fname.replace("\\", "/");
                        }
                        if (fname == sname) {
                            let text = q["data"];
                            this.dataqueue.splice(i, 1);
                            return text;
                        }
                    }
                    if (q["cmd"] == "onerror" && q["srccmd"] == "filelist") {
                        console.error(JSON.stringify(q));
                        throw new Error(JSON.stringify(q));
                    }
                }
            }
        });
    }
    static LoadTextFile(name) {
        return __awaiter$a(this, void 0, void 0, function* () {
            let sname = name;
            while (sname.includes("\\")) {
                sname = sname.replace("\\", "/");
            }
            this.api.postMessage({ "cmd": "fs_load_text", "data": name });
            while (true) {
                yield this.sleep(1);
                for (var i = 0; i < this.dataqueue.length; i++) {
                    let q = this.dataqueue[i];
                    if (q["cmd"] == "filedata") {
                        let fname = q["filename"];
                        while (fname.includes("\\")) {
                            fname = fname.replace("\\", "/");
                        }
                        if (fname == sname) {
                            let text = q["data"];
                            this.dataqueue.splice(i, 1);
                            return text;
                        }
                    }
                    if (q["cmd"] == "onerror" && q["srccmd"] == "fs_load_text") {
                        console.error(JSON.stringify(q));
                        throw new Error(JSON.stringify(q));
                    }
                }
            }
        });
    }
    static LoadBinaryFile(name) {
        return __awaiter$a(this, void 0, void 0, function* () {
            let sname = name;
            while (sname.includes("\\")) {
                sname = sname.replace("\\", "/");
            }
            this.api.postMessage({ "cmd": "fs_load_binary", "data": name });
            while (true) {
                yield this.sleep(1);
                for (var i = 0; i < this.dataqueue.length; i++) {
                    let q = this.dataqueue[i];
                    if (q["cmd"] == "filedata") {
                        let fname = q["filename"];
                        while (fname.includes("\\")) {
                            fname = fname.replace("\\", "/");
                        }
                        if (fname == sname) {
                            this.dataqueue.splice(i, 1);
                            let hex = q["data"];
                            return this.Str2Hex(hex);
                        }
                    }
                    if (q["cmd"] == "onerror" && q["srccmd"] == "fs_load_binary") {
                        console.error(JSON.stringify(q));
                        throw new Error(JSON.stringify(q));
                    }
                }
            }
        });
    }
    static SaveBinaryFile(name, data) {
        return __awaiter$a(this, void 0, void 0, function* () {
            this.api.postMessage({ "cmd": "fs_save_binary", "data": { "data": this.Hex2Str(data), "filename": name } });
        });
    }
    static SaveTextFile(name, data) {
        return __awaiter$a(this, void 0, void 0, function* () {
            this.api.postMessage({ "cmd": "fs_save_text", "data": { "data": data, "filename": name } });
        });
    }
}
IO.dataqueue = [];

class DomTool {
    static Init() {
        document.body.style.backgroundColor = "#000";
        document.body.style.color = "#fff";
        this.screen = new Screen();
    }
    static InitFullScreen() {
        document.body.style.backgroundColor = "#000";
        document.body.style.color = "#fff";
        document.body.style.width = "100%";
        document.body.style.height = "100%";
        document.body.style.position = "absolute";
        document.body.style.margin = "0px";
        document.body.style.padding = "0px";
        document.body.style.overflow = "hidden";
        document.body.style.transform = "scale(1,1)";
        document.body.style.userSelect = "none";
        window.addEventListener('mousewheel', function (e) {
            e = e || window.event;
            if ((e.wheelDelta && event.ctrlKey) || e.detail) {
                event.preventDefault();
            }
        }, { capture: false, passive: false });
        window.addEventListener('keydown', function (event) {
            if ((event.ctrlKey === true || event.metaKey === true)
                && (event.keyCode === 61 || event.keyCode === 107
                    || event.keyCode === 173 || event.keyCode === 109
                    || event.keyCode === 187 || event.keyCode === 189)) {
                event.preventDefault();
            }
        }, false);
        window.addEventListener('touchmove', function (event) {
            event.preventDefault();
        }, { passive: false });
        window.addEventListener('dragstart', function (event) {
            event.preventDefault();
        }, false);
        window.addEventListener("contextmenu", (e) => {
            e.preventDefault();
            return false;
        });
        window.addEventListener("mousedown", function (event) {
            if (event.button == 2) {
                event.preventDefault();
            }
        });
        this.screen = new Screen();
    }
    static AddPanel() {
        let div = new Panel();
        this.screen.AddChild(div);
        return div;
    }
    static AddLabel(text) {
        let label = new Label(text);
        this.screen.AddChild(label);
        return label;
    }
    static AddTextBox(title, deftext) {
        let text = new TextBox(title, deftext);
        this.screen.AddChild(text);
        return text;
    }
    static AddButton(text, onclick = null) {
        let btn = new Button(text);
        btn.SetClickEvent(onclick);
        this.screen.AddChild(btn);
        return btn;
    }
    static AddToggle(title, value) {
        let toggle = new Toggle(title, value);
        this.screen.AddChild(toggle);
        return toggle;
    }
    static AddSpliter() {
        let s = new Splitter();
        document.body.appendChild(s.getRoot());
        return s;
    }
}

class Rectangle {
    /**
     * Creates an instance of Rectangle.
     *
     * @param {number} [width=0]
     * @param {number} [height=0]
     * @param {number} [x=0]
     * @param {number} [y=0]
     * @param {boolean} [rot=false]
     * @param {boolean} [allowRotation=false]
     * @memberof Rectangle
     */
    constructor(width = 0, height = 0, x = 0, y = 0, rot = false, allowRotation = undefined) {
        /**
         * Oversized tag on rectangle which is bigger than packer itself.
         *
         * @type {boolean}
         * @memberof Rectangle
         */
        this.oversized = false;
        this._rot = false;
        this._allowRotation = undefined;
        this._dirty = 0;
        this._width = width;
        this._height = height;
        this._x = x;
        this._y = y;
        this._data = {};
        this._rot = rot;
        this._allowRotation = allowRotation;
    }
    /**
     * Test if two given rectangle collide each other
     *
     * @static
     * @param {IRectangle} first
     * @param {IRectangle} second
     * @returns
     * @memberof Rectangle
     */
    static Collide(first, second) { return first.collide(second); }
    /**
     * Test if the first rectangle contains the second one
     *
     * @static
     * @param {IRectangle} first
     * @param {IRectangle} second
     * @returns
     * @memberof Rectangle
     */
    static Contain(first, second) { return first.contain(second); }
    /**
     * Get the area (w * h) of the rectangle
     *
     * @returns {number}
     * @memberof Rectangle
     */
    area() { return this.width * this.height; }
    /**
     * Test if the given rectangle collide with this rectangle.
     *
     * @param {IRectangle} rect
     * @returns {boolean}
     * @memberof Rectangle
     */
    collide(rect) {
        return (rect.x < this.x + this.width &&
            rect.x + rect.width > this.x &&
            rect.y < this.y + this.height &&
            rect.y + rect.height > this.y);
    }
    /**
     * Test if this rectangle contains the given rectangle.
     *
     * @param {IRectangle} rect
     * @returns {boolean}
     * @memberof Rectangle
     */
    contain(rect) {
        return (rect.x >= this.x && rect.y >= this.y &&
            rect.x + rect.width <= this.x + this.width && rect.y + rect.height <= this.y + this.height);
    }
    get width() { return this._width; }
    set width(value) {
        if (value === this._width)
            return;
        this._width = value;
        this._dirty++;
    }
    get height() { return this._height; }
    set height(value) {
        if (value === this._height)
            return;
        this._height = value;
        this._dirty++;
    }
    get x() { return this._x; }
    set x(value) {
        if (value === this._x)
            return;
        this._x = value;
        this._dirty++;
    }
    get y() { return this._y; }
    set y(value) {
        if (value === this._y)
            return;
        this._y = value;
        this._dirty++;
    }
    /**
     * If the rectangle is rotated
     *
     * @type {boolean}
     * @memberof Rectangle
     */
    get rot() { return this._rot; }
    /**
     * Set the rotate tag of the rectangle.
     *
     * note: after `rot` is set, `width/height` of this rectangle is swaped.
     *
     * @memberof Rectangle
     */
    set rot(value) {
        if (this._allowRotation === false)
            return;
        if (this._rot !== value) {
            const tmp = this.width;
            this.width = this.height;
            this.height = tmp;
            this._rot = value;
            this._dirty++;
        }
    }
    /**
     * If the rectangle allow rotation
     *
     * @type {boolean}
     * @memberof Rectangle
     */
    get allowRotation() { return this._allowRotation; }
    /**
     * Set the allowRotation tag of the rectangle.
     *
     * @memberof Rectangle
     */
    set allowRotation(value) {
        if (this._allowRotation !== value) {
            this._allowRotation = value;
            this._dirty++;
        }
    }
    get data() { return this._data; }
    set data(value) {
        if (value === null || value === this._data)
            return;
        this._data = value;
        // extract allowRotation settings
        if (typeof value === "object" && value.hasOwnProperty("allowRotation")) {
            this._allowRotation = value.allowRotation;
        }
        this._dirty++;
    }
    get dirty() { return this._dirty > 0; }
    setDirty(value = true) { this._dirty = value ? this._dirty + 1 : 0; }
}

const EDGE_MAX_VALUE = 4096;
var PACKING_LOGIC;
(function (PACKING_LOGIC) {
    PACKING_LOGIC[PACKING_LOGIC["MAX_AREA"] = 0] = "MAX_AREA";
    PACKING_LOGIC[PACKING_LOGIC["MAX_EDGE"] = 1] = "MAX_EDGE";
})(PACKING_LOGIC || (PACKING_LOGIC = {}));
let Bin$1 = class Bin {
    constructor() {
        this._dirty = 0;
    }
    get dirty() { return this._dirty > 0 || this.rects.some(rect => rect.dirty); }
    /**
     * Set bin dirty status
     *
     * @memberof Bin
     */
    setDirty(value = true) {
        this._dirty = value ? this._dirty + 1 : 0;
        if (!value) {
            for (let rect of this.rects) {
                if (rect.setDirty)
                    rect.setDirty(false);
            }
        }
    }
};

class MaxRectsBin extends Bin$1 {
    constructor(maxWidth = EDGE_MAX_VALUE, maxHeight = EDGE_MAX_VALUE, padding = 0, options = {}) {
        super();
        this.maxWidth = maxWidth;
        this.maxHeight = maxHeight;
        this.padding = padding;
        this.freeRects = [];
        this.rects = [];
        this.verticalExpand = false;
        this.options = {
            smart: true,
            pot: true,
            square: true,
            allowRotation: false,
            tag: false,
            exclusiveTag: true,
            border: 0,
            logic: PACKING_LOGIC.MAX_EDGE
        };
        this.options = Object.assign(Object.assign({}, this.options), options);
        this.width = this.options.smart ? 0 : maxWidth;
        this.height = this.options.smart ? 0 : maxHeight;
        this.border = this.options.border ? this.options.border : 0;
        this.freeRects.push(new Rectangle(this.maxWidth + this.padding - this.border * 2, this.maxHeight + this.padding - this.border * 2, this.border, this.border));
        this.stage = new Rectangle(this.width, this.height);
    }
    add(...args) {
        let data;
        let rect;
        if (args.length === 1) {
            if (typeof args[0] !== 'object')
                throw new Error("MacrectsBin.add(): Wrong parameters");
            rect = args[0];
            // Check if rect.tag match bin.tag, if bin.tag not defined, it will accept any rect
            let tag = (rect.data && rect.data.tag) ? rect.data.tag : rect.tag ? rect.tag : undefined;
            if (this.options.tag && this.options.exclusiveTag && this.tag !== tag)
                return undefined;
        }
        else {
            data = args.length > 2 ? args[2] : null;
            // Check if data.tag match bin.tag, if bin.tag not defined, it will accept any rect
            if (this.options.tag && this.options.exclusiveTag) {
                if (data && this.tag !== data.tag)
                    return undefined;
                if (!data && this.tag)
                    return undefined;
            }
            rect = new Rectangle(args[0], args[1]);
            rect.data = data;
            rect.setDirty(false);
        }
        const result = this.place(rect);
        if (result)
            this.rects.push(result);
        return result;
    }
    repack() {
        let unpacked = [];
        this.reset();
        // re-sort rects from big to small
        this.rects.sort((a, b) => {
            const result = Math.max(b.width, b.height) - Math.max(a.width, a.height);
            if (result === 0 && a.hash && b.hash) {
                return a.hash > b.hash ? -1 : 1;
            }
            else
                return result;
        });
        for (let rect of this.rects) {
            if (!this.place(rect)) {
                unpacked.push(rect);
            }
        }
        for (let rect of unpacked)
            this.rects.splice(this.rects.indexOf(rect), 1);
        return unpacked.length > 0 ? unpacked : undefined;
    }
    reset(deepReset = false, resetOption = false) {
        if (deepReset) {
            if (this.data)
                delete this.data;
            if (this.tag)
                delete this.tag;
            this.rects = [];
            if (resetOption) {
                this.options = {
                    smart: true,
                    pot: true,
                    square: true,
                    allowRotation: false,
                    tag: false,
                    border: 0
                };
            }
        }
        this.width = this.options.smart ? 0 : this.maxWidth;
        this.height = this.options.smart ? 0 : this.maxHeight;
        this.border = this.options.border ? this.options.border : 0;
        this.freeRects = [new Rectangle(this.maxWidth + this.padding - this.border * 2, this.maxHeight + this.padding - this.border * 2, this.border, this.border)];
        this.stage = new Rectangle(this.width, this.height);
        this._dirty = 0;
    }
    clone() {
        let clonedBin = new MaxRectsBin(this.maxWidth, this.maxHeight, this.padding, this.options);
        for (let rect of this.rects) {
            clonedBin.add(rect);
        }
        return clonedBin;
    }
    place(rect) {
        // recheck if tag matched
        let tag = (rect.data && rect.data.tag) ? rect.data.tag : rect.tag ? rect.tag : undefined;
        if (this.options.tag && this.options.exclusiveTag && this.tag !== tag)
            return undefined;
        let node;
        let allowRotation;
        // getter/setter do not support hasOwnProperty()
        if (rect.hasOwnProperty("_allowRotation") && rect.allowRotation !== undefined) {
            allowRotation = rect.allowRotation; // Per Rectangle allowRotation override packer settings
        }
        else {
            allowRotation = this.options.allowRotation;
        }
        node = this.findNode(rect.width + this.padding, rect.height + this.padding, allowRotation);
        if (node) {
            this.updateBinSize(node);
            let numRectToProcess = this.freeRects.length;
            let i = 0;
            while (i < numRectToProcess) {
                if (this.splitNode(this.freeRects[i], node)) {
                    this.freeRects.splice(i, 1);
                    numRectToProcess--;
                    i--;
                }
                i++;
            }
            this.pruneFreeList();
            this.verticalExpand = this.width > this.height ? true : false;
            rect.x = node.x;
            rect.y = node.y;
            if (rect.rot === undefined)
                rect.rot = false;
            rect.rot = node.rot ? !rect.rot : rect.rot;
            this._dirty++;
            return rect;
        }
        else if (!this.verticalExpand) {
            if (this.updateBinSize(new Rectangle(rect.width + this.padding, rect.height + this.padding, this.width + this.padding - this.border, this.border)) || this.updateBinSize(new Rectangle(rect.width + this.padding, rect.height + this.padding, this.border, this.height + this.padding - this.border))) {
                return this.place(rect);
            }
        }
        else {
            if (this.updateBinSize(new Rectangle(rect.width + this.padding, rect.height + this.padding, this.border, this.height + this.padding - this.border)) || this.updateBinSize(new Rectangle(rect.width + this.padding, rect.height + this.padding, this.width + this.padding - this.border, this.border))) {
                return this.place(rect);
            }
        }
        return undefined;
    }
    findNode(width, height, allowRotation) {
        let score = Number.MAX_VALUE;
        let areaFit;
        let r;
        let bestNode;
        for (let i in this.freeRects) {
            r = this.freeRects[i];
            if (r.width >= width && r.height >= height) {
                areaFit = (this.options.logic === PACKING_LOGIC.MAX_AREA) ?
                    r.width * r.height - width * height :
                    Math.min(r.width - width, r.height - height);
                if (areaFit < score) {
                    bestNode = new Rectangle(width, height, r.x, r.y);
                    score = areaFit;
                }
            }
            if (!allowRotation)
                continue;
            // Continue to test 90-degree rotated rectangle
            if (r.width >= height && r.height >= width) {
                areaFit = (this.options.logic === PACKING_LOGIC.MAX_AREA) ?
                    r.width * r.height - height * width :
                    Math.min(r.height - width, r.width - height);
                if (areaFit < score) {
                    bestNode = new Rectangle(height, width, r.x, r.y, true); // Rotated node
                    score = areaFit;
                }
            }
        }
        return bestNode;
    }
    splitNode(freeRect, usedNode) {
        // Test if usedNode intersect with freeRect
        if (!freeRect.collide(usedNode))
            return false;
        // Do vertical split
        if (usedNode.x < freeRect.x + freeRect.width && usedNode.x + usedNode.width > freeRect.x) {
            // New node at the top side of the used node
            if (usedNode.y > freeRect.y && usedNode.y < freeRect.y + freeRect.height) {
                let newNode = new Rectangle(freeRect.width, usedNode.y - freeRect.y, freeRect.x, freeRect.y);
                this.freeRects.push(newNode);
            }
            // New node at the bottom side of the used node
            if (usedNode.y + usedNode.height < freeRect.y + freeRect.height) {
                let newNode = new Rectangle(freeRect.width, freeRect.y + freeRect.height - (usedNode.y + usedNode.height), freeRect.x, usedNode.y + usedNode.height);
                this.freeRects.push(newNode);
            }
        }
        // Do Horizontal split
        if (usedNode.y < freeRect.y + freeRect.height &&
            usedNode.y + usedNode.height > freeRect.y) {
            // New node at the left side of the used node.
            if (usedNode.x > freeRect.x && usedNode.x < freeRect.x + freeRect.width) {
                let newNode = new Rectangle(usedNode.x - freeRect.x, freeRect.height, freeRect.x, freeRect.y);
                this.freeRects.push(newNode);
            }
            // New node at the right side of the used node.
            if (usedNode.x + usedNode.width < freeRect.x + freeRect.width) {
                let newNode = new Rectangle(freeRect.x + freeRect.width - (usedNode.x + usedNode.width), freeRect.height, usedNode.x + usedNode.width, freeRect.y);
                this.freeRects.push(newNode);
            }
        }
        return true;
    }
    pruneFreeList() {
        // Go through each pair of freeRects and remove any rects that is redundant
        let i = 0;
        let j = 0;
        let len = this.freeRects.length;
        while (i < len) {
            j = i + 1;
            let tmpRect1 = this.freeRects[i];
            while (j < len) {
                let tmpRect2 = this.freeRects[j];
                if (tmpRect2.contain(tmpRect1)) {
                    this.freeRects.splice(i, 1);
                    i--;
                    len--;
                    break;
                }
                if (tmpRect1.contain(tmpRect2)) {
                    this.freeRects.splice(j, 1);
                    j--;
                    len--;
                }
                j++;
            }
            i++;
        }
    }
    updateBinSize(node) {
        if (!this.options.smart)
            return false;
        if (this.stage.contain(node))
            return false;
        let tmpWidth = Math.max(this.width, node.x + node.width - this.padding + this.border);
        let tmpHeight = Math.max(this.height, node.y + node.height - this.padding + this.border);
        if (this.options.allowRotation) {
            // do extra test on rotated node whether it's a better choice
            const rotWidth = Math.max(this.width, node.x + node.height - this.padding + this.border);
            const rotHeight = Math.max(this.height, node.y + node.width - this.padding + this.border);
            if (rotWidth * rotHeight < tmpWidth * tmpHeight) {
                tmpWidth = rotWidth;
                tmpHeight = rotHeight;
            }
        }
        if (this.options.pot) {
            tmpWidth = Math.pow(2, Math.ceil(Math.log(tmpWidth) * Math.LOG2E));
            tmpHeight = Math.pow(2, Math.ceil(Math.log(tmpHeight) * Math.LOG2E));
        }
        if (this.options.square) {
            tmpWidth = tmpHeight = Math.max(tmpWidth, tmpHeight);
        }
        if (tmpWidth > this.maxWidth + this.padding || tmpHeight > this.maxHeight + this.padding) {
            return false;
        }
        this.expandFreeRects(tmpWidth + this.padding, tmpHeight + this.padding);
        this.width = this.stage.width = tmpWidth;
        this.height = this.stage.height = tmpHeight;
        return true;
    }
    expandFreeRects(width, height) {
        this.freeRects.forEach((freeRect, index) => {
            if (freeRect.x + freeRect.width >= Math.min(this.width + this.padding - this.border, width)) {
                freeRect.width = width - freeRect.x - this.border;
            }
            if (freeRect.y + freeRect.height >= Math.min(this.height + this.padding - this.border, height)) {
                freeRect.height = height - freeRect.y - this.border;
            }
        }, this);
        this.freeRects.push(new Rectangle(width - this.width - this.padding, height - this.border * 2, this.width + this.padding - this.border, this.border));
        this.freeRects.push(new Rectangle(width - this.border * 2, height - this.height - this.padding, this.border, this.height + this.padding - this.border));
        this.freeRects = this.freeRects.filter(freeRect => {
            return !(freeRect.width <= 0 || freeRect.height <= 0 || freeRect.x < this.border || freeRect.y < this.border);
        });
        this.pruneFreeList();
    }
}

class OversizedElementBin extends Bin$1 {
    constructor(...args) {
        super();
        this.rects = [];
        if (args.length === 1) {
            if (typeof args[0] !== 'object')
                throw new Error("OversizedElementBin: Wrong parameters");
            const rect = args[0];
            this.rects = [rect];
            this.width = rect.width;
            this.height = rect.height;
            this.data = rect.data;
            rect.oversized = true;
        }
        else {
            this.width = args[0];
            this.height = args[1];
            this.data = args.length > 2 ? args[2] : null;
            const rect = new Rectangle(this.width, this.height);
            rect.oversized = true;
            rect.data = this.data;
            this.rects.push(rect);
        }
        this.freeRects = [];
        this.maxWidth = this.width;
        this.maxHeight = this.height;
        this.options = { smart: false, pot: false, square: false };
    }
    add() { return undefined; }
    reset(deepReset = false) {
        // nothing to do here
    }
    repack() { return undefined; }
    clone() {
        let clonedBin = new OversizedElementBin(this.rects[0]);
        return clonedBin;
    }
}

class MaxRectsPacker {
    /**
     * Creates an instance of MaxRectsPacker.
     *
     * @param {number} width of the output atlas (default is 4096)
     * @param {number} height of the output atlas (default is 4096)
     * @param {number} padding between glyphs/images (default is 0)
     * @param {IOption} [options={}] (Optional) packing options
     * @memberof MaxRectsPacker
     */
    constructor(width = EDGE_MAX_VALUE, height = EDGE_MAX_VALUE, padding = 0, options = {}) {
        this.width = width;
        this.height = height;
        this.padding = padding;
        /**
         * Options for MaxRect Packer
         *
         * @property {boolean} options.smart Smart sizing packer (default is true)
         * @property {boolean} options.pot use power of 2 sizing (default is true)
         * @property {boolean} options.square use square size (default is false)
         * @property {boolean} options.allowRotation allow rotation packing (default is false)
         * @property {boolean} options.tag allow auto grouping based on `rect.tag` (default is false)
         * @property {boolean} options.exclusiveTag tagged rects will have dependent bin, if set to `false`, packer will try to put tag rects into the same bin (default is true)
         * @property {boolean} options.border atlas edge spacing (default is 0)
         * @property {PACKING_LOGIC} options.logic MAX_AREA or MAX_EDGE based sorting logic (default is MAX_EDGE)
         * @export
         * @interface Option
         */
        this.options = {
            smart: true,
            pot: true,
            square: false,
            allowRotation: false,
            tag: false,
            exclusiveTag: true,
            border: 0,
            logic: PACKING_LOGIC.MAX_EDGE
        };
        this._currentBinIndex = 0;
        this.bins = [];
        this.options = Object.assign(Object.assign({}, this.options), options);
    }
    add(...args) {
        if (args.length === 1) {
            if (typeof args[0] !== 'object')
                throw new Error("MacrectsPacker.add(): Wrong parameters");
            const rect = args[0];
            if (rect.width > this.width || rect.height > this.height) {
                this.bins.push(new OversizedElementBin(rect));
            }
            else {
                let added = this.bins.slice(this._currentBinIndex).find(bin => bin.add(rect) !== undefined);
                if (!added) {
                    let bin = new MaxRectsBin(this.width, this.height, this.padding, this.options);
                    let tag = (rect.data && rect.data.tag) ? rect.data.tag : rect.tag ? rect.tag : undefined;
                    if (this.options.tag && tag)
                        bin.tag = tag;
                    bin.add(rect);
                    this.bins.push(bin);
                }
            }
            return rect;
        }
        else {
            const rect = new Rectangle(args[0], args[1]);
            if (args.length > 2)
                rect.data = args[2];
            if (rect.width > this.width || rect.height > this.height) {
                this.bins.push(new OversizedElementBin(rect));
            }
            else {
                let added = this.bins.slice(this._currentBinIndex).find(bin => bin.add(rect) !== undefined);
                if (!added) {
                    let bin = new MaxRectsBin(this.width, this.height, this.padding, this.options);
                    if (this.options.tag && rect.data.tag)
                        bin.tag = rect.data.tag;
                    bin.add(rect);
                    this.bins.push(bin);
                }
            }
            return rect;
        }
    }
    /**
     * Add an Array of bins/rectangles to the packer.
     *
     * `Javascript`: Any object has property: { width, height, ... } is accepted.
     *
     * `Typescript`: object shall extends `MaxrectsPacker.IRectangle`.
     *
     * note: object has `hash` property will have more stable packing result
     *
     * @param {IRectangle[]} rects Array of bin/rectangles
     * @memberof MaxRectsPacker
     */
    addArray(rects) {
        if (!this.options.tag || this.options.exclusiveTag) {
            // if not using tag or using exclusiveTag, old approach
            this.sort(rects, this.options.logic).forEach(rect => this.add(rect));
        }
        else {
            // sort rects by tags first
            if (rects.length === 0)
                return;
            rects.sort((a, b) => {
                const aTag = (a.data && a.data.tag) ? a.data.tag : a.tag ? a.tag : undefined;
                const bTag = (b.data && b.data.tag) ? b.data.tag : b.tag ? b.tag : undefined;
                return bTag === undefined ? -1 : aTag === undefined ? 1 : bTag > aTag ? -1 : 1;
            });
            // iterate all bins to find the first bin which can place rects with same tag
            //
            let currentTag;
            let currentIdx = 0;
            let targetBin = this.bins.slice(this._currentBinIndex).find((bin, binIndex) => {
                let testBin = bin.clone();
                for (let i = currentIdx; i < rects.length; i++) {
                    const rect = rects[i];
                    const tag = (rect.data && rect.data.tag) ? rect.data.tag : rect.tag ? rect.tag : undefined;
                    // initialize currentTag
                    if (i === 0)
                        currentTag = tag;
                    if (tag !== currentTag) {
                        // all current tag memeber tested successfully
                        currentTag = tag;
                        // do addArray()
                        this.sort(rects.slice(currentIdx, i), this.options.logic).forEach(r => bin.add(r));
                        currentIdx = i;
                        // recrusively addArray() with remaining rects
                        this.addArray(rects.slice(i));
                        return true;
                    }
                    // remaining untagged rect will use normal addArray()
                    if (tag === undefined) {
                        // do addArray()
                        this.sort(rects.slice(i), this.options.logic).forEach(r => this.add(r));
                        currentIdx = rects.length;
                        // end test
                        return true;
                    }
                    // still in the same tag group
                    if (testBin.add(rect) === undefined) {
                        // add the rects that could fit into the bins already
                        // do addArray()
                        this.sort(rects.slice(currentIdx, i), this.options.logic).forEach(r => bin.add(r));
                        currentIdx = i;
                        // current bin cannot contain all tag members
                        // procceed to test next bin
                        return false;
                    }
                }
                // all rects tested
                // do addArray() to the remaining tag group
                this.sort(rects.slice(currentIdx), this.options.logic).forEach(r => bin.add(r));
                return true;
            });
            // create a new bin if no current bin fit
            if (!targetBin) {
                const rect = rects[currentIdx];
                const bin = new MaxRectsBin(this.width, this.height, this.padding, this.options);
                const tag = (rect.data && rect.data.tag) ? rect.data.tag : rect.tag ? rect.tag : undefined;
                if (this.options.tag && this.options.exclusiveTag && tag)
                    bin.tag = tag;
                this.bins.push(bin);
                // Add the rect to the newly created bin
                bin.add(rect);
                currentIdx++;
                this.addArray(rects.slice(currentIdx));
            }
        }
    }
    /**
     * Reset entire packer to initial states, keep settings
     *
     * @memberof MaxRectsPacker
     */
    reset() {
        this.bins = [];
        this._currentBinIndex = 0;
    }
    /**
     * Repack all elements inside bins
     *
     * @param {boolean} [quick=true] quick repack only dirty bins
     * @returns {void}
     * @memberof MaxRectsPacker
     */
    repack(quick = true) {
        if (quick) {
            let unpack = [];
            for (let bin of this.bins) {
                if (bin.dirty) {
                    let up = bin.repack();
                    if (up)
                        unpack.push(...up);
                }
            }
            this.addArray(unpack);
            return;
        }
        if (!this.dirty)
            return;
        const allRects = this.rects;
        this.reset();
        this.addArray(allRects);
    }
    /**
     * Stop adding new element to the current bin and return a new bin.
     *
     * note: After calling `next()` all elements will no longer added to previous bins.
     *
     * @returns {Bin}
     * @memberof MaxRectsPacker
     */
    next() {
        this._currentBinIndex = this.bins.length;
        return this._currentBinIndex;
    }
    /**
     * Load bins to the packer, overwrite exist bins
     *
     * @param {MaxRectsBin[]} bins MaxRectsBin objects
     * @memberof MaxRectsPacker
     */
    load(bins) {
        bins.forEach((bin, index) => {
            if (bin.maxWidth > this.width || bin.maxHeight > this.height) {
                this.bins.push(new OversizedElementBin(bin.width, bin.height, {}));
            }
            else {
                let newBin = new MaxRectsBin(this.width, this.height, this.padding, bin.options);
                newBin.freeRects.splice(0);
                bin.freeRects.forEach((r, i) => {
                    newBin.freeRects.push(new Rectangle(r.width, r.height, r.x, r.y));
                });
                newBin.width = bin.width;
                newBin.height = bin.height;
                if (bin.tag)
                    newBin.tag = bin.tag;
                this.bins[index] = newBin;
            }
        }, this);
    }
    /**
     * Output current bins to save
     *
     * @memberof MaxRectsPacker
     */
    save() {
        let saveBins = [];
        this.bins.forEach((bin => {
            let saveBin = {
                width: bin.width,
                height: bin.height,
                maxWidth: bin.maxWidth,
                maxHeight: bin.maxHeight,
                freeRects: [],
                rects: [],
                options: bin.options
            };
            if (bin.tag)
                saveBin = Object.assign(Object.assign({}, saveBin), { tag: bin.tag });
            bin.freeRects.forEach(r => {
                saveBin.freeRects.push({
                    x: r.x,
                    y: r.y,
                    width: r.width,
                    height: r.height
                });
            });
            saveBins.push(saveBin);
        }));
        return saveBins;
    }
    /**
     * Sort the given rects based on longest edge or surface area.
     *
     * If rects have the same sort value, will sort by second key `hash` if presented.
     *
     * @private
     * @param {T[]} rects
     * @param {PACKING_LOGIC} [logic=PACKING_LOGIC.MAX_EDGE] sorting logic, "area" or "edge"
     * @returns
     * @memberof MaxRectsPacker
     */
    sort(rects, logic = PACKING_LOGIC.MAX_EDGE) {
        return rects.slice().sort((a, b) => {
            const result = (logic === PACKING_LOGIC.MAX_EDGE) ?
                Math.max(b.width, b.height) - Math.max(a.width, a.height) :
                b.width * b.height - a.width * a.height;
            if (result === 0 && a.hash && b.hash) {
                return a.hash > b.hash ? -1 : 1;
            }
            else
                return result;
        });
    }
    /**
     * Return current functioning bin index, perior to this wont accept any new elements
     *
     * @readonly
     * @type {number}
     * @memberof MaxRectsPacker
     */
    get currentBinIndex() { return this._currentBinIndex; }
    /**
     * Returns dirty status of all child bins
     *
     * @readonly
     * @type {boolean}
     * @memberof MaxRectsPacker
     */
    get dirty() { return this.bins.some(bin => bin.dirty); }
    /**
     * Return all rectangles in this packer
     *
     * @readonly
     * @type {T[]}
     * @memberof MaxRectsPacker
     */
    get rects() {
        let allRects = [];
        for (let bin of this.bins) {
            allRects.push(...bin.rects);
        }
        return allRects;
    }
}

var __awaiter$9 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
class TTImgData {
    constructor() {
        this.borderX1 = 0;
        this.borderY1 = 0;
        this.borderX2 = 0;
        this.borderY2 = 0;
    }
}
//Target2D是对ImageBitmap的封装，由于他的设计有点2b，所以封装一下
class Target2D {
    constructor(width, height) {
        this._canvas = new OffscreenCanvas(256, 256);
        this._canvas = new OffscreenCanvas(width, height);
        this._r2d = this._canvas.getContext("2d", { willReadFrequently: true });
        this._canvas.transferToImageBitmap();
        this._r2d.imageSmoothingEnabled = false;
    }
    getWidth() {
        return this._canvas.width;
    }
    getHeight() {
        return this._canvas.height;
    }
    setWidth(width) {
        this._canvas.width = width;
    }
    setHeight(height) {
        this._canvas.height = height;
    }
    getContext() {
        return this._r2d;
    }
    getCanvas() {
        return this._canvas;
    }
    getBitmap() {
        return this.bitmap;
    }
    //每次UpdateBitmap之后，应该要引发重绘
    updateBitmap() {
        // if (renderFunc != null)
        //     renderFunc();
        if (this.bitmap = null)
            this.bitmap.close(); //先灭掉上一个bitmap
        this.bitmap = this._canvas.transferToImageBitmap();
    }
}
class ImgTool {
    static Bin2Url(bin) {
        let blob = new Blob([bin]);
        return URL.createObjectURL(blob);
    }
    static Url2ImgAsync(url) {
        return new Promise((resolve, reject) => {
            let img = document.createElement("img");
            img.src = url;
            img.onload = () => {
                resolve(img);
            };
            img.onerror = () => {
                reject("img load fail.");
            };
        });
    }
    static Bin2ImgAsync(bin) {
        return __awaiter$9(this, void 0, void 0, function* () {
            let img = yield this.Url2ImgAsync(this.Bin2Url(bin));
            return img;
        });
    }
    static CreateImgData(width, height) {
        return this._backrender.getContext().createImageData(width, height);
    }
    static Bin2ImgDataAsync(bin) {
        return __awaiter$9(this, void 0, void 0, function* () {
            let img = yield this.Bin2ImgAsync(bin);
            return this.Img2Data(img);
        });
    }
    static Img2Data(img) {
        this._backrender.setWidth(img.width);
        this._backrender.setHeight(img.height);
        this._backrender.getContext().drawImage(img, 0, 0);
        let imgdata = this._backrender.getContext().getImageData(0, 0, this._backrender.getWidth(), this._backrender.getHeight());
        return imgdata;
    }
    static Canvas2PngFileDataAsync() {
        return __awaiter$9(this, void 0, void 0, function* () {
            let b = yield this._backrender.getCanvas().convertToBlob({ "type": "image/png" });
            let ab = yield b.arrayBuffer();
            return new Uint8Array(ab);
        });
    }
    static ImgData2BlobAsync(data) {
        return __awaiter$9(this, void 0, void 0, function* () {
            this._backrender.setWidth(data.width);
            this._backrender.setHeight(data.height);
            this._backrender.getContext().putImageData(data, 0, 0);
            let b = yield this._backrender.getCanvas().convertToBlob({ "type": "image/png" });
            return b;
        });
    }
    static ImgData2PngFileDataAsync(data) {
        return __awaiter$9(this, void 0, void 0, function* () {
            this._backrender.setWidth(data.width);
            this._backrender.setHeight(data.height);
            this._backrender.getContext().putImageData(data, 0, 0);
            return yield this.Canvas2PngFileDataAsync();
        });
    }
    static PutToImgData(target, src, x, y) {
        return __awaiter$9(this, void 0, void 0, function* () {
            for (var j = 0; j < src.height; j++) {
                for (var i = 0; i < src.width; i++) {
                    var dx = i + x;
                    var dy = j + y;
                    if (dx < 0 || dx >= target.width)
                        continue;
                    if (dy < 0 || dy >= target.height)
                        continue;
                    var srci = (j * src.width + i) * 4;
                    var desti = (dy * target.width + dx) * 4;
                    target.data[desti + 0] = src.data[srci + 0];
                    target.data[desti + 1] = src.data[srci + 1];
                    target.data[desti + 2] = src.data[srci + 2];
                    target.data[desti + 3] = src.data[srci + 3];
                }
            }
        });
    }
    static ImgFill(src) {
        let img = new TTImgData();
        {
            img.width = src.width;
            img.height = src.height;
            img.data = new Uint8Array(src.width * src.height * 4);
            for (var i = 0; i < img.data.length; i++) {
                img.data[i] = src.data[i];
            }
        }
        return img;
    }
    static ImgCut(src) {
        let x1 = src.width;
        let x2 = 0;
        let y1 = src.width;
        let y2 = 0;
        for (var y = 0; y < src.height; y++) {
            for (var x = 0; x < src.width; x++) {
                let a = src.data[(y * src.width + x) * 4 + 3];
                if (a > 0) {
                    if (x < x1)
                        x1 = x;
                    if (y < y1)
                        y1 = y;
                    if (x > x2)
                        x2 = x;
                    if (y > y2)
                        y2 = y;
                }
            }
        }
        let img = new TTImgData();
        img.borderX1 = x1;
        img.borderY1 = y1;
        img.borderX2 = src.width - 1 - x2;
        img.borderY2 = src.height - 1 - y2;
        let width = img.width = x2 - x1 + 1;
        let height = img.height = y2 - y1 + 1;
        img.data = new Uint8Array(width * height * 4);
        for (var sy = 0; sy < height; sy++) {
            for (var sx = 0; sx < width; sx++) {
                let srcseek = ((sy + y1) * src.width + (sx + x1)) * 4;
                let destseek = (sy * width + sx) * 4;
                img.data[destseek + 0] = src.data[srcseek + 0];
                img.data[destseek + 1] = src.data[srcseek + 1];
                img.data[destseek + 2] = src.data[srcseek + 2];
                img.data[destseek + 3] = src.data[srcseek + 3];
            }
        }
        return img;
    }
    static Pack(width, height, border, rects) {
        let mr = new MaxRectsPacker(width, height, border);
        mr.addArray(rects);
        let bins = [];
        for (var i = 0; i < mr.bins.length; i++) {
            let bin = new Bin();
            for (var j = 0; j < mr.bins[i].rects.length; j++) {
                bin.rects.push(mr.bins[i].rects[j]);
            }
            bins.push(bin);
        }
        return bins;
    }
}
ImgTool._backrender = new Target2D(256, 256);
class Bin {
    constructor() {
        this.rects = [];
    }
}

var __awaiter$8 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var tt;
(function (tt) {
    function sleep(ms) {
        return __awaiter$8(this, void 0, void 0, function* () {
            return new Promise(resolve => setTimeout(resolve, ms));
        });
    }
    tt.sleep = sleep;
    //#region   ------Code From Color
    class Color {
        constructor(r, g, b, a = 1) {
            this.R = r;
            this.G = g;
            this.B = b;
            this.A = a;
        }
        static get White() {
            return new Color(1, 1, 1, 1);
        }
        static get Black() {
            return new Color(0, 0, 0, 1);
        }
        static get Tran() {
            return new Color(0, 0, 0, 0);
        }
        Clone() {
            return new Color(this.R, this.G, this.B, this.A);
        }
    }
    tt.Color = Color;
    //不一定用的到，保证RGBA取值的玩意儿罢了
    class Color32 {
        constructor(r, g, b, a = 255) {
            this.data = new Uint8Array(4);
            this.R = r;
            this.G = g;
            this.B = b;
            this.A = a;
        }
        get R() {
            return this.data[0];
        }
        set R(v) {
            this.data[0] = v;
        }
        get G() {
            return this.data[1];
        }
        set G(v) {
            this.data[1] = v;
        }
        get B() {
            return this.data[2];
        }
        set B(v) {
            this.data[2] = v;
        }
        get A() {
            return this.data[3];
        }
        set A(v) {
            this.data[3] = v;
        }
    }
    tt.Color32 = Color32;
    //#endregion
    //#region  -----Code From Vector
    class Vector2 {
        constructor(x, y) {
            this.X = x;
            this.Y = y;
        }
        Clone() {
            return new Vector2(this.X, this.Y);
        }
        static get One() {
            return new Vector2(1, 1);
        }
        static get Zero() {
            return new Vector2(0, 0);
        }
        static Length(v1) {
            return Math.sqrt((v1.X) * (v1.X) + (v1.Y) * (v1.Y));
        }
        static Dist(v1, v2) {
            return Math.sqrt((v1.X - v2.X) * (v1.X - v2.X) + (v1.Y - v2.Y) * (v1.Y - v2.Y));
        }
        static Dir(from, to) {
            let dist = Vector2.Dist(from, to);
            return new Vector2((to.X - from.X) / dist, (to.Y - from.Y) / dist);
        }
        static Add(v1, v2) {
            return new Vector2(v1.X + v2.X, v1.Y + v2.Y);
        }
        static Normal(src) {
            let len = Vector2.Length(src);
            return new Vector2(src.X / len, src.Y / len);
        }
    }
    tt.Vector2 = Vector2;
    class Vector3 {
        constructor(x, y, z) {
            this.X = x;
            this.Y = y;
            this.Z = z;
        }
        Clone() {
            return new Vector3(this.X, this.Y, this.Z);
        }
        static get One() {
            return new Vector3(1, 1, 1);
        }
        static get Zero() {
            return new Vector3(0, 0, 0);
        }
    }
    tt.Vector3 = Vector3;
    //#endregion 
    //#region  -----Code From DrawPoint
    class DrawPoint {
        constructor() {
            //pos
            this.x = 0; //offset 0
            this.y = 0; //offset 4
            this.z = 0; //offset 8
            //color (byte)
            this.r = 0; //offset 12
            this.g = 0; //offset 13
            this.b = 0; //offset 14
            this.a = 0; //offset 15
            //uv
            this.u = 0; //offset 16
            this.v = 0; //offset 20
            //tex&paluv (byte)
            this.palx = 0; //offset 24
            this.paly = 0; //offset 25
            this.anyz = 0; //offset 26 //this can be auto,no need to public
            this.eff = 0; //offset 27
        }
        //pixel length =28
        Clone() {
            var p = new DrawPoint();
            p.x = this.x;
            p.y = this.y;
            p.z = this.z;
            p.u = this.u;
            p.v = this.v;
            p.r = this.r;
            p.g = this.g;
            p.b = this.b;
            p.a = this.a;
            p.palx = this.palx;
            p.paly = this.paly;
            p.anyz = this.anyz;
            p.eff = this.eff;
            p.x = this.x;
            return p;
        }
    }
    tt.DrawPoint = DrawPoint;
    //#endregion
    //#region -----Code From Rectangle
    class Rectangle {
        constructor(x, y, w, h) {
            this.X = x;
            this.Y = y;
            this.Width = w;
            this.Height = h;
        }
    }
    tt.Rectangle = Rectangle;
    class RectangleMath {
        static Clone(r) {
            return new Rectangle(r.X, r.Y, r.Width, r.Height);
        }
        static Intersect(left, r) {
            let x1 = left.X;
            let x2 = left.X + left.Width;
            let y1 = left.Y;
            let y2 = left.Y + left.Height;
            let rx1 = r.X;
            let ry1 = r.Y;
            let rx2 = rx1 + r.Width;
            let ry2 = ry1 + r.Height;
            if (x1 >= rx2 || rx1 >= x2 || y1 >= ry2 || ry1 >= y2)
                return new Rectangle(0, 0, 0, 0);
            let nx1 = Math.max(x1, rx1);
            let ny1 = Math.max(y1, ry1);
            let nx2 = Math.min(x2, rx2);
            let ny2 = Math.min(y2, ry2);
            return new Rectangle(nx1, ny1, nx2 - nx1, ny2 - ny1);
        }
    }
    tt.RectangleMath = RectangleMath;
    class Border {
        constructor(xLeft, yTop, xRight, yBottom) {
            this.XLeft = xLeft;
            this.YTop = yTop;
            this.XRight = xRight;
            this.YBottom = yBottom;
        }
    }
    tt.Border = Border;
    class UVRect {
        constructor(u1, v1, u2, v2) {
            this.U1 = u1;
            this.V1 = v1;
            this.U2 = u2;
            this.V2 = v2;
        }
    }
    tt.UVRect = UVRect;
    //#endregion
    //#region   -----Code From Graphics
    let RenderEffect;
    (function (RenderEffect) {
        RenderEffect[RenderEffect["RGBA"] = 0] = "RGBA";
        RenderEffect[RenderEffect["Gray"] = 1] = "Gray";
        RenderEffect[RenderEffect["PAL8"] = 2] = "PAL8";
        RenderEffect[RenderEffect["P5A3"] = 3] = "P5A3";
        RenderEffect[RenderEffect["GrayAsAlpha"] = 4] = "GrayAsAlpha";
    })(RenderEffect = tt.RenderEffect || (tt.RenderEffect = {}));
    let TextureFormat;
    (function (TextureFormat) {
        TextureFormat[TextureFormat["R8"] = 0] = "R8";
        TextureFormat[TextureFormat["RGBA32"] = 1] = "RGBA32";
    })(TextureFormat = tt.TextureFormat || (tt.TextureFormat = {}));
    //#endregion
    //#region  -----Code From Input
    class InputPoint {
        constructor() {
            this.id = 0;
            this.x = 0;
            this.y = 0;
            this.press = false;
            this.move = false;
        }
        Clone() {
            let p = new InputPoint();
            p.id = this.id;
            p.x = this.x;
            p.y = this.y;
            p.press = this.press;
            return p;
        }
    }
    tt.InputPoint = InputPoint;
    //#endregion
    class ImageBuffer {
    }
    tt.ImageBuffer = ImageBuffer;
    //#endregion
    //#region  PathTool
    class FileNameObj {
        constructor(filenameonly, longext) {
            this.filename = filenameonly;
            this.ext = longext;
            this.dotcount = 0;
            for (var i = 0; i < longext.length; i++) {
                if (longext.charAt(i) == ".")
                    this.dotcount++;
            }
            this.nextLevel = this.NextLevel();
        }
        NextLevel() {
            if (this.dotcount <= 1)
                return null;
            let pp = this.ext.indexOf('.', 1);
            let moreext = this.ext.substring(0, pp);
            let _ext = this.ext.substring(pp, this.ext.length);
            let _filename = this.filename + moreext;
            return new FileNameObj(_filename, _ext);
        }
    }
    tt.FileNameObj = FileNameObj;
    class PathTool {
        static CheckPath(url) {
            let nurl = url;
            while (nurl.indexOf('\\') >= 0) {
                nurl = nurl.replace('\\', '/');
            }
            if (nurl.charAt(nurl.length - 1) == '/')
                nurl = nurl.substring(0, nurl.length - 1);
            if (nurl.indexOf("./") == 0)
                nurl = nurl.substring(2, nurl.length);
            return nurl;
        }
        static GetPathName(url) {
            var i = url.lastIndexOf('/');
            if (i < 0)
                return url;
            else
                return url.substring(0, i);
        }
        static GetFileName(url) {
            var i = url.lastIndexOf('/');
            if (i < 0)
                return url;
            else
                return url.substring(i + 1, url.length);
        }
        static GetExt(filename) {
            if (filename.indexOf("\\") >= 0 || filename.indexOf("/") >= 0)
                throw new Error("error filename");
            let pp = filename.lastIndexOf('.');
            if (pp <= 0) {
                return "";
            }
            return filename.substring(pp, filename.length);
        }
        static GetLongExt(filename) {
            if (filename.indexOf("\\") >= 0 || filename.indexOf("/") >= 0)
                throw new Error("error filename");
            let pp = filename.indexOf('.', 0);
            if (pp <= 0) {
                return "";
            }
            return filename.substring(pp, filename.length);
        }
        static GetFileNameObj(filename) {
            let fname = this.GetFileName(filename);
            let pp = fname.indexOf('.', 0);
            if (pp < 1)
                return new FileNameObj(fname, "");
            let _filename = fname.substring(0, pp);
            let _ext = fname.substring(pp, fname.length);
            return new FileNameObj(_filename, _ext);
        }
    }
    tt.PathTool = PathTool;
    //#endregion
    //#region  binreader
    class StringConv {
        static ArrayToString_Utf8(array) {
            var ret = [];
            for (var i = 0; i < array.length; i++) {
                var cc = array[i];
                if (cc == 0)
                    break;
                var ct = 0;
                if (cc > 0xE0) {
                    ct = (cc & 0x0F) << 12;
                    cc = array[++i];
                    ct |= (cc & 0x3F) << 6;
                    cc = array[++i];
                    ct |= cc & 0x3F;
                    ret.push(String.fromCharCode(ct));
                }
                else if (cc > 0xC0) {
                    ct = (cc & 0x1F) << 6;
                    cc = array[++i];
                    ct |= (cc & 0x3F) << 6;
                    ret.push(String.fromCharCode(ct));
                }
                else if (cc > 0x80) {
                    throw new Error("InvalidCharacterError");
                }
                else {
                    ret.push(String.fromCharCode(array[i]));
                }
            }
            return ret.join('');
        }
        static ArrayToString_Ansi(array) {
            var ret = [];
            for (var i = 0; i < array.length; i++) {
                ret[i] = String.fromCharCode(array[i]);
            }
            return ret.join('');
        }
        static StringToArray_Ansi(str) {
            var bstr = [];
            for (var i = 0; i < str.length; i++) {
                bstr.push(str.charCodeAt(i));
            }
            return bstr;
        }
        static StringToArray_Utf8(str) {
            var bstr = [];
            for (var i = 0; i < str.length; i++) {
                var c = str.charAt(i);
                var cc = c.charCodeAt(0);
                if (cc > 0xFFFF) {
                    throw new Error("InvalidCharacterError");
                }
                if (cc > 0x80) {
                    if (cc < 0x07FF) {
                        var c1 = (cc >>> 6) | 0xC0;
                        var c2 = (cc & 0x3F) | 0x80;
                        bstr.push(c1, c2);
                    }
                    else {
                        var c1 = (cc >>> 12) | 0xE0;
                        var c2 = ((cc >>> 6) & 0x3F) | 0x80;
                        var c3 = (cc & 0x3F) | 0x80;
                        bstr.push(c1, c2, c3);
                    }
                }
                else {
                    bstr.push(cc);
                }
            }
            return bstr;
        }
    }
    tt.StringConv = StringConv;
    class BinReader {
        constructor(buf, seek = 0) {
            this._seek = seek;
            this._data = new DataView(buf, seek);
        }
        set position(seek) {
            this._seek = seek;
        }
        get position() {
            return this._seek;
        }
        length() {
            return this._data.byteLength;
        }
        canread() {
            //LogManager.Warn(this._buf.byteLength + "  &&&&&&&&&&&   " + this._seek + "    " + this._buf.buffer.byteLength);
            return this._data.byteLength - this._seek;
        }
        readSingle() {
            var num = this._data.getFloat32(this._seek, true);
            this._seek += 4;
            return num;
        }
        readDouble() {
            var num = this._data.getFloat64(this._seek, true);
            this._seek += 8;
            return num;
        }
        readInt8() {
            var num = this._data.getInt8(this._seek);
            this._seek += 1;
            return num;
        }
        readUInt8() {
            //LogManager.Warn(this._data.byteLength + "  @@@@@@@@@@@@@@@@@  " + this._seek);
            var num = this._data.getUint8(this._seek);
            this._seek += 1;
            return num;
        }
        readInt16() {
            //LogManager.Log(this._seek + "   " + this.length());
            var num = this._data.getInt16(this._seek, true);
            this._seek += 2;
            return num;
        }
        readUInt16() {
            var num = this._data.getUint16(this._seek, true);
            this._seek += 2;
            //LogManager.Warn("readUInt16 " + this._seek);
            return num;
        }
        readInt32() {
            var num = this._data.getInt32(this._seek, true);
            this._seek += 4;
            return num;
        }
        readUInt32() {
            var num = this._data.getUint32(this._seek, true);
            this._seek += 4;
            return num;
        }
        readUint8Array(target, offset = 0, length = -1) {
            if (length < 0)
                length = target.length;
            for (var i = 0; i < length; i++) {
                target[i] = this._data.getUint8(this._seek);
                this._seek++;
            }
        }
        readBoolean() {
            return this.readUInt8() > 0;
        }
    }
    tt.BinReader = BinReader;
    class BinWriter {
        constructor() {
            //if (buf == null)
            {
                var buf = new ArrayBuffer(1024);
                this._length = 0;
            }
            this._buf = new Uint8Array(buf);
            this._data = new DataView(this._buf.buffer);
            this._seek = 0;
        }
        get length() {
            return this._seek;
        }
        sureData(addlen) {
            var nextlen = this._buf.byteLength;
            while (nextlen < (this._length + addlen)) {
                nextlen += 1024;
            }
            if (nextlen != this._buf.byteLength) {
                var newbuf = new Uint8Array(nextlen);
                for (var i = 0; i < this._length; i++) {
                    newbuf[i] = this._buf[i];
                }
                this._buf = newbuf;
                this._data = new DataView(this._buf.buffer);
            }
            this._length += addlen;
        }
        getBuffer() {
            return this._buf.buffer.slice(0, this._seek);
        }
        set position(seek) {
            this._seek = seek;
        }
        get position() {
            return this._seek;
        }
        writeInt8(num) {
            this.sureData(1);
            this._data.setInt8(this._seek, num);
            this._seek++;
        }
        writeUInt8(num) {
            this.sureData(1);
            this._data.setUint8(this._seek, num);
            this._seek++;
        }
        writeInt16(num) {
            this.sureData(2);
            this._data.setInt16(this._seek, num, true);
            this._seek += 2;
        }
        writeUInt16(num) {
            this.sureData(2);
            this._data.setUint16(this._seek, num, true);
            this._seek += 2;
        }
        writeInt32(num) {
            this.sureData(4);
            this._data.setInt32(this._seek, num, true);
            this._seek += 4;
        }
        writeUInt32(num) {
            this.sureData(4);
            this._data.setUint32(this._seek, num, true);
            this._seek += 4;
        }
        writeSingle(num) {
            this.sureData(4);
            this._data.setFloat32(this._seek, num, true);
            this._seek += 4;
        }
        writeDouble(num) {
            this.sureData(8);
            this._data.setFloat64(this._seek, num, true);
            this._seek += 8;
        }
        writeBoolean(v) {
            this.writeUInt8(v == true ? 1 : 0);
        }
        writeUint8Array(array, offset = 0, length = -1) {
            if (length < 0)
                length = array.length;
            this.sureData(length);
            for (var i = offset; i < offset + length; i++) {
                this._data.setUint8(this._seek, array[i]);
                this._seek++;
            }
        }
    }
    tt.BinWriter = BinWriter;
    //#endregion
    //#region ttfont
    const font_magic = "TTFontV1";
    class TTFontHeader {
        constructor() {
            this.charCount = 0;
            this.fontSize = 0;
            this.fontBorder = 0; //可以加一个边，bit
        }
        get fontBitmapSize() {
            return this.fontSize + this.fontBorder;
        }
        Read(stream) {
            let buf = new Uint8Array(8);
            stream.readUint8Array(buf, 0, 8);
            if (StringConv.ArrayToString_Ansi(buf) != font_magic)
                throw new Error("not ttfont file.");
            this.charCount = stream.readUInt32();
            this.fontSize = stream.readUInt8();
            this.fontBorder = stream.readUInt8();
            if (this.charCount == 0 || this.charCount > 1000000)
                throw new Error("error char count.");
            if (this.fontSize < 8 || this.fontSize > 128)
                throw new Error("error fontSize");
            if (this.fontBorder > 128)
                throw new Error("error fontBorder");
        }
    }
    tt.TTFontHeader = TTFontHeader;
    class TTFontItem {
        constructor(bitmapsize) {
            this.charCode = 0;
            this.width = 0;
            this.height = 0;
            this.emptyX = 0;
            this.emptyY = 0;
            this.advanceX = 0;
            this.graydata = new Uint8Array(bitmapsize * bitmapsize);
        }
        Read(stream) {
            this.charCode = stream.readUInt32();
            this.width = stream.readUInt8();
            this.height = stream.readUInt8();
            this.emptyX = stream.readInt8();
            this.emptyY = stream.readInt8();
            this.advanceX = stream.readUInt8();
            stream.readUint8Array(this.graydata, 0, this.graydata.length);
        }
    }
    tt.TTFontItem = TTFontItem;
    class TTFontData {
        constructor() {
            this.header = new TTFontHeader();
            this.fontitems = {};
        }
        Load(buf) {
            var stream = new tt.BinReader(buf, 0);
            this.header.Read(stream);
            for (var i = 0; i < this.header.charCount; i++) {
                let item = new TTFontItem(this.header.fontBitmapSize);
                item.Read(stream);
                this.fontitems[item.charCode] = item;
            }
        }
    }
    tt.TTFontData = TTFontData;
    //#endregion 
    //#region  image
    const img_magic = "TTIMG_V1";
    let TTIMGFormat;
    (function (TTIMGFormat) {
        TTIMGFormat[TTIMGFormat["P5A3"] = 0] = "P5A3";
        TTIMGFormat[TTIMGFormat["PAL8"] = 1] = "PAL8";
        TTIMGFormat[TTIMGFormat["GRAY"] = 2] = "GRAY";
        TTIMGFormat[TTIMGFormat["RGBA"] = 3] = "RGBA";
    })(TTIMGFormat = tt.TTIMGFormat || (tt.TTIMGFormat = {}));
    class PalData {
        constructor(color32) {
            this.color32 = color32;
            this.paldata = new Uint8Array(color32 ? 32 * 4 : 256 * 4);
        }
        CanMerge(data) {
            let count = this.color32 ? 32 : 256;
            for (var i = this.color32 ? 0 : 1; i < count; i++) {
                let equal = (data.paldata[i * 4 + 0] == data.paldata[i * 4 + 0]
                    && data.paldata[i * 4 + 1] == data.paldata[i * 4 + 1]
                    && data.paldata[i * 4 + 2] == data.paldata[i * 4 + 2]
                    && data.paldata[i * 4 + 3] == data.paldata[i * 4 + 3]);
                if (equal)
                    continue;
                let lzero = (this.paldata[i * 4 + 0] == 0 && this.paldata[i * 4 + 1] == 0 && this.paldata[i * 4 + 2] == 0 && this.paldata[i * 4 + 3] == 0);
                let rzero = (data.paldata[i * 4 + 0] == 0 && data.paldata[i * 4 + 1] == 0 && data.paldata[i * 4 + 2] == 0 && data.paldata[i * 4 + 3] == 0);
                if (lzero || rzero)
                    continue;
                return false;
            }
            return true;
        }
        TryMerge(data) {
            let canm = this.CanMerge(data);
            if (!canm)
                return false;
            let count = this.color32 ? 32 : 256;
            for (var i = this.color32 ? 0 : 1; i < count; i++) {
                let lzero = (this.paldata[i * 4 + 0] == 0 && this.paldata[i * 4 + 1] == 0 && this.paldata[i * 4 + 2] == 0 && this.paldata[i * 4 + 3] == 0);
                let rzero = (data.paldata[i * 4 + 0] == 0 && data.paldata[i * 4 + 1] == 0 && data.paldata[i * 4 + 2] == 0 && data.paldata[i * 4 + 3] == 0);
                if (lzero && !rzero) {
                    this.paldata[i * 4 + 0] = data.paldata[i * 4 + 0];
                    this.paldata[i * 4 + 1] = data.paldata[i * 4 + 1];
                    this.paldata[i * 4 + 2] = data.paldata[i * 4 + 2];
                    this.paldata[i * 4 + 3] = data.paldata[i * 4 + 3];
                }
            }
            return true;
        }
        Clone() {
            var pdat = new PalData(this.color32);
            for (var i = 0; i < pdat.paldata.length; i++) {
                pdat.paldata[i] = this.paldata[i];
            }
            return pdat;
        }
    }
    tt.PalData = PalData;
    class TTImgData {
        constructor() {
            this.width = 0;
            this.height = 0;
            this.format = 0;
            this.data = null;
            this.pals = [];
        }
        GetRgbaData(palindex = 0) {
            if (this.data == null)
                throw new Error("data is null.");
            if (this.format == TTIMGFormat.RGBA) {
                return this.data;
            }
            else {
                var outdata = new Uint8Array(this.width * this.height * 4);
                for (var i = 0; i < this.data.length; i++) {
                    var s = this.data[i];
                    if (this.format == TTIMGFormat.GRAY) {
                        outdata[i * 4 + 0] = s;
                        outdata[i * 4 + 1] = s;
                        outdata[i * 4 + 2] = s;
                        outdata[i * 4 + 3] = s;
                    }
                    if (this.format == TTIMGFormat.PAL8) {
                        outdata[i * 4 + 0] = this.pals[palindex].paldata[s * 4 + 0];
                        outdata[i * 4 + 1] = this.pals[palindex].paldata[s * 4 + 1];
                        outdata[i * 4 + 2] = this.pals[palindex].paldata[s * 4 + 2];
                        outdata[i * 4 + 3] = this.pals[palindex].paldata[s * 4 + 3];
                    }
                    if (this.format == TTIMGFormat.P5A3) {
                        let p = s % 32;
                        outdata[i * 4 + 0] = this.pals[palindex].paldata[p * 4 + 0];
                        outdata[i * 4 + 1] = this.pals[palindex].paldata[p * 4 + 1];
                        outdata[i * 4 + 2] = this.pals[palindex].paldata[p * 4 + 2];
                        outdata[i * 4 + 3] = this.pals[palindex].paldata[p * 4 + 3];
                    }
                }
                return outdata;
            }
        }
        Load(buf) {
            var stream = new tt.BinReader(buf, 0);
            var magicarr = new Uint8Array(8);
            stream.readUint8Array(magicarr, 0, 8);
            if (img_magic != StringConv.ArrayToString_Ansi(magicarr))
                throw new Error("not ttimg file");
            this.format = stream.readUInt8();
            if (this.format > TTIMGFormat.RGBA)
                throw new Error("error format");
            let needpal = (this.format == TTIMGFormat.PAL8 || this.format == TTIMGFormat.P5A3);
            var palcount = stream.readUInt8();
            if (!needpal && palcount > 0)
                throw new Error("donot need pal but got >0.");
            if (needpal && palcount == 0)
                throw new Error("need pal but got =0.");
            for (var i = 0; i < palcount; i++) {
                stream.readUInt8();
                let pal = new PalData(this.format == TTIMGFormat.P5A3);
                if (this.format == TTIMGFormat.PAL8) {
                    pal.paldata = new Uint8Array(256 * 4);
                    stream.readUint8Array(pal.paldata, 0, 256 * 4);
                }
                else if (this.format == TTIMGFormat.P5A3) {
                    pal.paldata = new Uint8Array(32 * 4);
                    stream.readUint8Array(pal.paldata, 0, 32 * 4);
                }
                else
                    throw new Error("shoud not be here.");
                this.pals.push(pal);
            }
            this.width = stream.readUInt16();
            this.height = stream.readUInt16();
            let bytelen = this.format == TTIMGFormat.RGBA ? 4 : 1;
            this.data = new Uint8Array(this.width * this.height * bytelen);
            stream.readUint8Array(this.data, 0, this.data.length);
        }
    }
    tt.TTImgData = TTImgData;
    //#endregion
    //#region  Sprite
    class Sprite {
        constructor(tex, texpal) {
            this.tex = tex;
            this.texpal = texpal;
            this.effect = RenderEffect.RGBA;
            this.uv = new UVRect(0, 0, 1, 1);
            this.border = new Border(0, 0, 0, 0);
            this.pixelwidth = tex.getWidth();
            this.pixelheight = tex.getHeight();
            this.paluvs = [];
        }
        //total width
        get totalWidth() {
            return this.pixelwidth + this.border.XLeft + this.border.XRight;
        }
        //total height
        get totalHeight() {
            return this.pixelheight + this.border.YTop + this.border.YBottom;
        }
        RenderRectWithLimit(batcher, rect, limitRect, color = null, palindex = -1) {
            let rectbuf = Sprite._rectbuf;
            while (rectbuf.length < 4) {
                rectbuf.push(new tt.DrawPoint());
            }
            if (Sprite._colorbuf == null) {
                Sprite._colorbuf = Color.White;
            }
            let _color = color == null ? Sprite._colorbuf : color;
            let palu = 0;
            let palv = 0;
            if (palindex >= 0) {
                palu = this.paluvs[palindex].X;
                palv = this.paluvs[palindex].Y;
            }
            let sx = rect.Width / this.totalWidth;
            let sy = rect.Height / this.totalHeight;
            let x1 = rect.X + sx * this.border.XLeft;
            let x2 = rect.X + rect.Width - sx * this.border.XRight;
            let y1 = rect.Y + sy * this.border.YTop;
            let y2 = rect.Y + rect.Height - sy * this.border.YBottom;
            let u1 = this.uv.U1;
            let v1 = this.uv.V1;
            let u2 = this.uv.U2;
            let v2 = this.uv.V2;
            let wx = x2 - x1;
            let wy = y2 - y1;
            //检查是否整体抹去
            if (x1 >= limitRect.X + limitRect.Width || x2 < limitRect.X
                ||
                    y1 >= limitRect.Y + limitRect.Height || y2 < limitRect.Y) {
                x1 = x2 = y1 = y2 = 0;
            }
            else if (wx != 0 && wy != 0) {
                let nx1 = x1;
                let nx2 = x2;
                let ny1 = y1;
                let ny2 = y2;
                let change = false;
                if (x1 < limitRect.X) {
                    nx1 = limitRect.X;
                    change = true;
                }
                if (x2 > limitRect.X + limitRect.Width) {
                    nx2 = limitRect.X + limitRect.Width;
                    change = true;
                }
                if (y1 < limitRect.Y) {
                    ny1 = limitRect.Y;
                    change = true;
                }
                if (y2 > limitRect.Y + limitRect.Height) {
                    ny2 = limitRect.Y + limitRect.Height;
                    change = true;
                }
                if (change) {
                    let uw = u2 - u1;
                    let vw = v2 - v1;
                    let sx1 = (nx1 - x1) / wx;
                    let sx2 = (nx2 - x2) / wx;
                    let sy1 = (ny1 - y1) / wy;
                    let sy2 = (ny2 - y2) / wy;
                    u1 = u1 + uw * sx1;
                    v1 = v1 + vw * sy1;
                    u2 = u2 + uw * sx2;
                    v2 = v2 + vw * sy2;
                    x1 = nx1;
                    y1 = ny1;
                    x2 = nx2;
                    y2 = ny2;
                }
            }
            rectbuf[0].x = x1;
            rectbuf[0].y = y1;
            rectbuf[0].u = u1;
            rectbuf[0].v = v1;
            rectbuf[0].r = _color.R;
            rectbuf[0].g = _color.G;
            rectbuf[0].b = _color.B;
            rectbuf[0].a = _color.A;
            rectbuf[0].palx = palu;
            rectbuf[0].paly = palv;
            rectbuf[0].eff = this.effect;
            rectbuf[1].x = x2;
            rectbuf[1].y = y1;
            rectbuf[1].u = u2;
            rectbuf[1].v = v1;
            rectbuf[1].r = _color.R;
            rectbuf[1].g = _color.G;
            rectbuf[1].b = _color.B;
            rectbuf[1].a = _color.A;
            rectbuf[1].palx = palu;
            rectbuf[1].paly = palv;
            rectbuf[1].eff = this.effect;
            rectbuf[2].x = x1;
            rectbuf[2].y = y2;
            rectbuf[2].u = u1;
            rectbuf[2].v = v2;
            rectbuf[2].r = _color.R;
            rectbuf[2].g = _color.G;
            rectbuf[2].b = _color.B;
            rectbuf[2].a = _color.A;
            rectbuf[2].palx = palu;
            rectbuf[2].paly = palv;
            rectbuf[2].eff = this.effect;
            rectbuf[3].x = x2;
            rectbuf[3].y = y2;
            rectbuf[3].u = u2;
            rectbuf[3].v = v2;
            rectbuf[3].r = _color.R;
            rectbuf[3].g = _color.G;
            rectbuf[3].b = _color.B;
            rectbuf[3].a = _color.A;
            rectbuf[3].palx = palu;
            rectbuf[3].paly = palv;
            rectbuf[3].eff = this.effect;
            batcher.DrawQuads(this.tex, null, this.texpal, rectbuf, 1);
        }
        RenderRect(batcher, rect, color = null, palindex = -1) {
            let rectbuf = Sprite._rectbuf;
            while (rectbuf.length < 4) {
                rectbuf.push(new tt.DrawPoint());
            }
            if (Sprite._colorbuf == null) {
                Sprite._colorbuf = Color.White;
            }
            let _color = color == null ? Sprite._colorbuf : color;
            let palu = 0;
            let palv = 0;
            if (palindex >= 0) {
                palu = this.paluvs[palindex].X;
                palv = this.paluvs[palindex].Y;
            }
            let sx = rect.Width / this.totalWidth;
            let sy = rect.Height / this.totalHeight;
            rectbuf[0].x = rect.X + sx * this.border.XLeft;
            rectbuf[0].y = rect.Y + sy * this.border.YTop;
            rectbuf[0].u = this.uv.U1;
            rectbuf[0].v = this.uv.V1;
            rectbuf[0].r = _color.R;
            rectbuf[0].g = _color.G;
            rectbuf[0].b = _color.B;
            rectbuf[0].a = _color.A;
            rectbuf[0].palx = palu;
            rectbuf[0].paly = palv;
            rectbuf[0].eff = this.effect;
            rectbuf[1].x = rect.X + rect.Width - sx * this.border.XRight;
            rectbuf[1].y = rect.Y + sy * this.border.YTop;
            rectbuf[1].u = this.uv.U2;
            rectbuf[1].v = this.uv.V1;
            rectbuf[1].r = _color.R;
            rectbuf[1].g = _color.G;
            rectbuf[1].b = _color.B;
            rectbuf[1].a = _color.A;
            rectbuf[1].palx = palu;
            rectbuf[1].paly = palv;
            rectbuf[1].eff = this.effect;
            rectbuf[2].x = rect.X + sx * this.border.XLeft;
            rectbuf[2].y = rect.Y + rect.Height - sy * this.border.YBottom;
            rectbuf[2].u = this.uv.U1;
            rectbuf[2].v = this.uv.V2;
            rectbuf[2].r = _color.R;
            rectbuf[2].g = _color.G;
            rectbuf[2].b = _color.B;
            rectbuf[2].a = _color.A;
            rectbuf[2].palx = palu;
            rectbuf[2].paly = palv;
            rectbuf[2].eff = this.effect;
            rectbuf[3].x = rect.X + rect.Width - sx * this.border.XRight;
            rectbuf[3].y = rect.Y + rect.Height - sy * this.border.YBottom;
            rectbuf[3].u = this.uv.U2;
            rectbuf[3].v = this.uv.V2;
            rectbuf[3].r = _color.R;
            rectbuf[3].g = _color.G;
            rectbuf[3].b = _color.B;
            rectbuf[3].a = _color.A;
            rectbuf[3].palx = palu;
            rectbuf[3].paly = palv;
            rectbuf[3].eff = this.effect;
            batcher.DrawQuads(this.tex, null, this.texpal, rectbuf, 1);
        }
        RenderRect2(batcher, x1, y1, x2, y2, color = null, palindex = -1) {
            let rectbuf = Sprite._rectbuf;
            while (rectbuf.length < 4) {
                rectbuf.push(new tt.DrawPoint());
            }
            if (Sprite._colorbuf == null) {
                Sprite._colorbuf = Color.White;
            }
            let _color = color == null ? Sprite._colorbuf : color;
            let palu = 0;
            let palv = 0;
            if (palindex >= 0) {
                palu = this.paluvs[palindex].X;
                palv = this.paluvs[palindex].Y;
            }
            let sx = (x2 - x1) / this.totalWidth;
            let sy = (y2 - y1) / this.totalHeight;
            rectbuf[0].x = x1 + sx * this.border.XLeft;
            rectbuf[0].y = y1 + sy * this.border.YTop;
            rectbuf[0].u = this.uv.U1;
            rectbuf[0].v = this.uv.V1;
            rectbuf[0].r = _color.R;
            rectbuf[0].g = _color.G;
            rectbuf[0].b = _color.B;
            rectbuf[0].a = _color.A;
            rectbuf[0].palx = palu;
            rectbuf[0].paly = palv;
            rectbuf[0].eff = this.effect;
            rectbuf[1].x = x2 - sx * this.border.XRight;
            rectbuf[1].y = y1 + sy * this.border.YTop;
            rectbuf[1].u = this.uv.U2;
            rectbuf[1].v = this.uv.V1;
            rectbuf[1].r = _color.R;
            rectbuf[1].g = _color.G;
            rectbuf[1].b = _color.B;
            rectbuf[1].a = _color.A;
            rectbuf[1].palx = palu;
            rectbuf[1].paly = palv;
            rectbuf[1].eff = this.effect;
            rectbuf[2].x = x1 + sx * this.border.XLeft;
            rectbuf[2].y = y2 - sy * this.border.YBottom;
            rectbuf[2].u = this.uv.U1;
            rectbuf[2].v = this.uv.V2;
            rectbuf[2].r = _color.R;
            rectbuf[2].g = _color.G;
            rectbuf[2].b = _color.B;
            rectbuf[2].a = _color.A;
            rectbuf[2].palx = palu;
            rectbuf[2].paly = palv;
            rectbuf[2].eff = this.effect;
            rectbuf[3].x = x2 - sx * this.border.XRight;
            rectbuf[3].y = y2 - sy * this.border.YBottom;
            rectbuf[3].u = this.uv.U2;
            rectbuf[3].v = this.uv.V2;
            rectbuf[3].r = _color.R;
            rectbuf[3].g = _color.G;
            rectbuf[3].b = _color.B;
            rectbuf[3].a = _color.A;
            rectbuf[3].palx = palu;
            rectbuf[3].paly = palv;
            rectbuf[3].eff = this.effect;
            batcher.DrawQuads(this.tex, null, this.texpal, rectbuf, 1);
        }
        Render(batcher, pos, scale, color = null, palindex = -1) {
            let rectbuf = Sprite._rectbuf;
            while (rectbuf.length < 4) {
                rectbuf.push(new tt.DrawPoint());
            }
            if (Sprite._colorbuf == null) {
                Sprite._colorbuf = Color.White;
            }
            let _color = color == null ? Sprite._colorbuf : color;
            let palu = 0;
            let palv = 0;
            if (palindex >= 0) {
                palu = this.paluvs[palindex].X;
                palv = this.paluvs[palindex].Y;
            }
            //  let rect = new Rectangle(x, y, s.totalWidth * scale.X, s.totalHeight * scale.Y);
            //   rectbuf[0].x = rect.X + sx * this.border.XLeft;
            //   rectbuf[0].y = rect.Y + sy * this.border.YTop;
            // rectbuf[1].x = rect.X + rect.Width - sx * this.border.XRight;
            // rectbuf[1].y = rect.Y + sy * this.border.YTop;
            rectbuf[0].x = pos.X + scale.X * this.border.XLeft;
            rectbuf[0].y = pos.Y + scale.Y * this.border.YTop;
            rectbuf[0].u = this.uv.U1;
            rectbuf[0].v = this.uv.V1;
            rectbuf[0].r = _color.R;
            rectbuf[0].g = _color.G;
            rectbuf[0].b = _color.B;
            rectbuf[0].a = _color.A;
            rectbuf[0].palx = palu;
            rectbuf[0].paly = palv;
            rectbuf[0].eff = this.effect;
            //(this.totalWidth - this.border.XRight) * scale.X;
            rectbuf[1].x = pos.X + scale.X * (this.totalWidth - this.border.XRight);
            rectbuf[1].y = pos.Y + scale.Y * this.border.YTop;
            rectbuf[1].u = this.uv.U2;
            rectbuf[1].v = this.uv.V1;
            rectbuf[1].r = _color.R;
            rectbuf[1].g = _color.G;
            rectbuf[1].b = _color.B;
            rectbuf[1].a = _color.A;
            rectbuf[1].palx = palu;
            rectbuf[1].paly = palv;
            rectbuf[1].eff = this.effect;
            rectbuf[2].x = pos.X + scale.X * this.border.XLeft;
            rectbuf[2].y = pos.Y + scale.Y * (this.totalHeight - this.border.YBottom);
            rectbuf[2].u = this.uv.U1;
            rectbuf[2].v = this.uv.V2;
            rectbuf[2].r = _color.R;
            rectbuf[2].g = _color.G;
            rectbuf[2].b = _color.B;
            rectbuf[2].a = _color.A;
            rectbuf[2].palx = palu;
            rectbuf[2].paly = palv;
            rectbuf[2].eff = this.effect;
            rectbuf[3].x = pos.X + scale.X * (this.totalWidth - this.border.XRight);
            rectbuf[3].y = pos.Y + scale.Y * (this.totalHeight - this.border.YBottom);
            rectbuf[3].u = this.uv.U2;
            rectbuf[3].v = this.uv.V2;
            rectbuf[3].r = _color.R;
            rectbuf[3].g = _color.G;
            rectbuf[3].b = _color.B;
            rectbuf[3].a = _color.A;
            rectbuf[3].palx = palu;
            rectbuf[3].paly = palv;
            rectbuf[3].eff = this.effect;
            batcher.DrawQuads(this.tex, null, this.texpal, rectbuf, 1);
        }
        RenderWithRotate(batcher, pos, scale, rotate, rotate_povit_x, rotate_povit_y, color = null, palindex = -1) {
            let rectbuf = Sprite._rectbuf;
            while (rectbuf.length < 4) {
                rectbuf.push(new tt.DrawPoint());
            }
            if (Sprite._colorbuf == null) {
                Sprite._colorbuf = Color.White;
            }
            let _color = color == null ? Sprite._colorbuf : color;
            let palu = 0;
            let palv = 0;
            if (palindex >= 0) {
                palu = this.paluvs[palindex].X;
                palv = this.paluvs[palindex].Y;
            }
            let x1 = pos.X + scale.X * this.border.XLeft;
            let x2 = pos.X + scale.X * (this.totalWidth - this.border.XRight);
            let y1 = pos.Y + scale.Y * this.border.YTop;
            let y2 = pos.Y + scale.Y * (this.totalHeight - this.border.YBottom);
            //旋转逻辑
            let cx = pos.X + rotate_povit_x * scale.X;
            let cy = pos.Y + rotate_povit_y * scale.Y; //中心点
            let dx1 = x1 - cx; //偏移
            let dy1 = y1 - cy;
            let dx2 = x2 - cx;
            let dy2 = y2 - cy;
            //矩阵变换
            let sinr = Math.sin(rotate);
            let cosr = Math.cos(rotate);
            let p0x = cx + dx1 * cosr - dy1 * sinr;
            let p0y = cy + dx1 * sinr + dy1 * cosr;
            let p1x = cx + dx2 * cosr - dy1 * sinr;
            let p1y = cy + dx2 * sinr + dy1 * cosr;
            let p2x = cx + dx1 * cosr - dy2 * sinr;
            let p2y = cy + dx1 * sinr + dy2 * cosr;
            let p3x = cx + dx2 * cosr - dy2 * sinr;
            let p3y = cy + dx2 * sinr + dy2 * cosr;
            rectbuf[0].x = p0x;
            rectbuf[0].y = p0y;
            rectbuf[0].u = this.uv.U1;
            rectbuf[0].v = this.uv.V1;
            rectbuf[0].r = _color.R;
            rectbuf[0].g = _color.G;
            rectbuf[0].b = _color.B;
            rectbuf[0].a = _color.A;
            rectbuf[0].palx = palu;
            rectbuf[0].paly = palv;
            rectbuf[0].eff = this.effect;
            //(this.totalWidth - this.border.XRight) * scale.X;
            rectbuf[1].x = p1x;
            rectbuf[1].y = p1y;
            rectbuf[1].u = this.uv.U2;
            rectbuf[1].v = this.uv.V1;
            rectbuf[1].r = _color.R;
            rectbuf[1].g = _color.G;
            rectbuf[1].b = _color.B;
            rectbuf[1].a = _color.A;
            rectbuf[1].palx = palu;
            rectbuf[1].paly = palv;
            rectbuf[1].eff = this.effect;
            rectbuf[2].x = p2x;
            rectbuf[2].y = p2y;
            rectbuf[2].u = this.uv.U1;
            rectbuf[2].v = this.uv.V2;
            rectbuf[2].r = _color.R;
            rectbuf[2].g = _color.G;
            rectbuf[2].b = _color.B;
            rectbuf[2].a = _color.A;
            rectbuf[2].palx = palu;
            rectbuf[2].paly = palv;
            rectbuf[2].eff = this.effect;
            rectbuf[3].x = p3x;
            rectbuf[3].y = p3y;
            rectbuf[3].u = this.uv.U2;
            rectbuf[3].v = this.uv.V2;
            rectbuf[3].r = _color.R;
            rectbuf[3].g = _color.G;
            rectbuf[3].b = _color.B;
            rectbuf[3].a = _color.A;
            rectbuf[3].palx = palu;
            rectbuf[3].paly = palv;
            rectbuf[3].eff = this.effect;
            batcher.DrawQuads(this.tex, null, this.texpal, rectbuf, 1);
        }
    }
    Sprite._rectbuf = [];
    tt.Sprite = Sprite;
    //#endregion
    //#region  Atalas
    class Atlas {
        constructor() {
            this.allsprite = [];
            this.mapsprite = {};
        }
        ParseJson(json, ignoreCase = true) {
            let ss = json["sprites"];
            let texsize = json["texsize"].split(",");
            let w = parseInt(texsize[0]);
            let h = parseInt(texsize[1]);
            for (var i = 0; i < ss.length; i++) {
                let s = new tt.Sprite(this.tex, null);
                let name = ss[i]["name"];
                if (ignoreCase) {
                    name = name.toLowerCase();
                }
                let border = ss[i]["border"].split(",");
                let uv = ss[i]["uv"].split(",");
                //let pivot = (ss[i]["pivot"] as string).split(",");
                this.mapsprite[name] = this.allsprite.length;
                this.allsprite.push(s);
                s.border.XLeft = parseInt(border[0]);
                s.border.YTop = parseInt(border[1]);
                s.border.XRight = parseInt(border[2]);
                s.border.YBottom = parseInt(border[3]);
                let ux = parseInt(uv[0]);
                let uy = parseInt(uv[1]);
                s.pixelwidth = parseInt(uv[2]);
                s.pixelheight = parseInt(uv[3]);
                s.uv.U1 = (ux - 0.01) / w;
                s.uv.V1 = (uy - 0.01) / h;
                s.uv.U2 = (ux + s.pixelwidth - 0.01) / w;
                s.uv.V2 = (uy + s.pixelheight - 0.01) / h;
            }
        }
        GetSprite(name) {
            return this.allsprite[this.mapsprite[name]];
        }
    }
    tt.Atlas = Atlas;
    //#endregion
    //#region  texturepool
    class FillTexture {
        constructor(size = 2048, gray) {
            this.tex = tt.graphic.CreateDynamicTexture(size, size, gray ? TextureFormat.R8 : TextureFormat.RGBA32);
            this.fillX = 0;
            this.fillY = 0;
            this.lineHeight = 0;
            this._dirty = false;
        }
        HaveSpace(width, height) {
            if (width > this.tex.getWidth() || height > this.tex.getHeight())
                return false;
            if (this.fillX + width > this.tex.getWidth()) { //need new line
                return (this.fillY + this.lineHeight + height <= this.tex.getHeight());
            }
            else {
                return (this.fillY + height <= this.tex.getHeight());
            }
        }
        AddSprite_ByFontItem(fontitem, paltex) {
            if (this.HaveSpace(fontitem.width, fontitem.height) == false)
                throw new Error("no space.");
            let u1 = this.fillX;
            let v1 = this.fillY;
            if (this.fillX + fontitem.width > this.tex.getWidth()) { //need new line
                u1 = 0;
                this.fillX = fontitem.width;
                this.fillY += this.lineHeight;
                v1 = this.fillY;
                this.lineHeight = fontitem.height;
            }
            else {
                this.fillX += fontitem.width;
                if (this.lineHeight < fontitem.height)
                    this.lineHeight = fontitem.height;
            }
            this.tex.UploadTexture(u1, v1, fontitem.width, fontitem.height, fontitem.graydata);
            //this.tex.ApplyTexture(false);
            let s = new Sprite(this.tex, paltex);
            s.tex = this.tex;
            s.effect = RenderEffect.GrayAsAlpha;
            u1 -= 0.01;
            v1 -= 0.01;
            let u2 = u1 + fontitem.width - 0.01;
            let v2 = v1 + fontitem.height - 0.01;
            s.uv = new UVRect(u1 / this.tex.getWidth(), v1 / this.tex.getHeight(), u2 / this.tex.getWidth(), v2 / this.tex.getHeight());
            s.pixelwidth = fontitem.width;
            s.pixelheight = fontitem.height;
            s.border.XLeft = fontitem.emptyX;
            s.border.XRight = fontitem.advanceX - fontitem.emptyX - fontitem.width;
            s.border.YTop = fontitem.emptyY;
            s.border.YBottom = 0;
            if (s.border.XLeft < 0)
                s.border.XLeft = 0;
            if (s.border.XRight < 0)
                s.border.XRight = 0;
            if (s.border.YTop < 0)
                s.border.YTop = 0;
            this._dirty = true;
            return s;
        }
        AddSprite_ByImg(img, paltex) {
            if (this.HaveSpace(img.width, img.height) == false)
                throw new Error("no space.");
            if (img.data == null)
                throw new Error("error img.");
            let u1 = this.fillX;
            let v1 = this.fillY;
            if (this.fillX + img.width > this.tex.getWidth()) { //need new line
                u1 = 0;
                this.fillX = img.width;
                this.fillY += this.lineHeight;
                v1 = this.fillY;
                this.lineHeight = img.height;
            }
            else {
                this.fillX += img.width;
                if (this.lineHeight < img.height)
                    this.lineHeight = img.height;
            }
            this.tex.UploadTexture(u1, v1, img.width, img.height, img.data);
            //this.tex.ApplyTexture(false);
            let s = new Sprite(this.tex, paltex);
            s.tex = this.tex;
            if (img.format == TTIMGFormat.RGBA)
                throw new Error("no way.");
            if (img.format == TTIMGFormat.PAL8)
                s.effect = RenderEffect.PAL8;
            if (img.format == TTIMGFormat.P5A3)
                s.effect = RenderEffect.P5A3;
            if (img.format == TTIMGFormat.GRAY)
                s.effect = RenderEffect.Gray;
            let u2 = u1 + img.width;
            let v2 = v1 + img.height;
            s.uv = new UVRect(u1 / this.tex.getWidth(), v1 / this.tex.getHeight(), u2 / this.tex.getWidth(), v2 / this.tex.getHeight());
            s.pixelwidth = img.width;
            s.pixelheight = img.height;
            this._dirty = true;
            return s;
        }
        Apply() {
            if (this._dirty) {
                this.tex.ApplyTexture(false);
                this._dirty = false;
            }
        }
    }
    class Texture8Pool {
        constructor(texsize = 2048) {
            this.texs = [];
            this._size = texsize;
            this.texs.push(new FillTexture(texsize, true));
            this._paltexture = tt.graphic.CreateDynamicTexture(256, 256, TextureFormat.RGBA32);
            this._palline = [];
            this._allpals = [];
            this._allpaluvs = [];
        }
        get PaletteTexture() {
            return this._paltexture;
        }
        ApplyLast() {
            let last = this.texs[this.texs.length - 1];
            last.Apply();
        }
        ApplyAll() {
            for (var i = 0; i < this.texs.length; i++) {
                this.texs[i].Apply();
            }
        }
        AddSprite_ByFontItem(fontitem) {
            if (fontitem.width > this._size || fontitem.height > this._size)
                throw new Error("img is too large.");
            let last = this.texs[this.texs.length - 1];
            if (!last.HaveSpace(fontitem.width, fontitem.height)) {
                last.Apply();
                last = new FillTexture(this._size, true);
                this.texs.push(last);
            }
            let sprite = last.AddSprite_ByFontItem(fontitem, this._paltexture);
            return sprite;
        }
        AddSprite_ByImg(img) {
            if (img.width > this._size || img.height > this._size)
                throw new Error("img is too large.");
            let last = this.texs[this.texs.length - 1];
            if (!last.HaveSpace(img.width, img.height)) {
                last.Apply();
                last = new FillTexture(this._size, true);
                this.texs.push(last);
            }
            let sprite = last.AddSprite_ByImg(img, this._paltexture);
            for (var i = 0; i < img.pals.length; i++) {
                var paluv = this.AddPal(img.pals[i]);
                sprite.paluvs.push(paluv);
            }
            return sprite;
        }
        AddPal(data) {
            for (var i = 0; i < this._allpals.length; i++) {
                if (this._allpals[i].TryMerge(data)) {
                    return this._allpaluvs[i].Clone();
                }
            }
            let y = 0;
            let x = 0;
            if (data.color32) {
                for (y = 0; y < this._palline.length; y++) {
                    let line = this._palline[y];
                    let right = line.col[line.col.length - 1];
                    if (256 - right >= 32) {
                        x = right + 32;
                        break;
                    }
                }
            }
            if (y >= 256) {
                throw new Error("no free pal pos.");
            }
            this._paltexture.UploadTexture(x, y, data.color32 ? 32 : 256, 1, data.paldata);
            this._paltexture.ApplyTexture(false);
            this._allpals.push(data.Clone());
            this._allpaluvs.push(new Vector2(x, y));
            return new Vector2(x, y);
        }
    }
    tt.Texture8Pool = Texture8Pool;
    //#endregion
    //#region  font
    class Font {
        constructor(packer, fontdata) {
            this._mapSprites = {};
            this._pool = packer;
            this._fontdata = fontdata;
        }
        GetFontSize() {
            return this._fontdata.header.fontSize;
        }
        GetCharSprite(charCode) {
            let ss = this._mapSprites[charCode];
            if (ss != undefined)
                return ss;
            let fitem = this._fontdata.fontitems[charCode];
            if (fitem == undefined)
                return null;
            var s = this._pool.AddSprite_ByFontItem(fitem);
            this._mapSprites[charCode] = s;
            return s;
        }
        SureText(text, scale = 1.0) {
            let w = 0;
            for (var i = 0; i < text.length; i++) {
                let s = this.GetCharSprite(text.charCodeAt(i));
                if (s == null) {
                    w += this._fontdata.header.fontSize / 2 * scale;
                }
                else {
                    w += s.totalWidth * scale;
                }
            }
            this._pool.ApplyAll();
            return w;
        }
        RenderText(bathcer, text, pos, scale, color) {
            //tt.platform.Log("==render"+text);
            let xadd = 0;
            for (var i = 0; i < text.length; i++) {
                let s = this.GetCharSprite(text.charCodeAt(i));
                if (s != null) {
                    //let rect = new Rectangle(pos.X + xadd, pos.Y, s.totalWidth * scale.X, s.totalHeight * scale.Y);
                    //s.RenderRect(bathcer, rect, color)
                    s.Render(bathcer, new Vector2(pos.X + xadd, pos.Y), scale, color);
                    xadd += (s.totalWidth * scale.X);
                }
                else {
                    xadd += this._fontdata.header.fontSize / 2 * scale.X;
                }
            }
        }
        RenderTextWithLimit(bathcer, text, pos, scale, color, limitRect) {
            let xadd = 0;
            for (var i = 0; i < text.length; i++) {
                let s = this.GetCharSprite(text.charCodeAt(i));
                if (s != null) {
                    let rect = new Rectangle(pos.X + xadd, pos.Y, s.totalWidth * scale.X, s.totalHeight * scale.Y);
                    s.RenderRectWithLimit(bathcer, rect, limitRect, color);
                    //s.Render(bathcer, new Vector2(pos.X + xadd, pos.Y), scale, color);
                    xadd += (s.totalWidth * scale.X);
                }
                else {
                    xadd += this._fontdata.header.fontSize / 2 * scale.X;
                }
            }
        }
    }
    tt.Font = Font;
    //#endregion
})(tt || (tt = {}));

var tt_impl$b;
(function (tt_impl) {
    class MainScreen {
        constructor(webgl) {
            this.rectLimit = [];
            this._webgl = webgl;
            console.log("webgl init size:" + webgl.canvas.width + "," + webgl.canvas.height);
            this.ClearColor = new tt.Color(1, 0, 1, 1);
        }
        IsMainOutput() {
            return true;
        }
        Begin() {
            this._webgl.bindFramebuffer(this._webgl.FRAMEBUFFER, null);
            this._webgl.viewport(0, 0, this.getWidth(), this.getHeight());
            this._webgl.clearColor(this.ClearColor.R, this.ClearColor.G, this.ClearColor.B, this.ClearColor.A);
            this._webgl.clear(this._webgl.COLOR_BUFFER_BIT);
            this.ResetLimitRect();
        }
        End() {
            this._webgl.flush();
        }
        getID() {
            return 0;
        }
        getFormat() {
            return tt.TextureFormat.RGBA32;
        }
        getWidth() {
            return this._webgl.canvas.width;
        }
        getHeight() {
            return this._webgl.canvas.height;
        }
        getData() {
            throw new Error("not support.");
        }
        Resize(width, height) {
            throw new Error("not support.");
        }
        IsStatic() {
            return true;
        }
        IsTarget() {
            return true;
        }
        UploadTexture(x, y, w, h, data) {
            throw new Error("MainScreen Method not implemented.");
        }
        ApplyTexture(TurnToStatic) {
            throw new Error("MainScreen Method not implemented.");
        }
        Destory() {
            throw new Error("MainScreen Method not implemented.");
        }
        PushLimitRect(rect) {
            let newrect = null;
            if (this.rectLimit.length == 0)
                newrect = tt.RectangleMath.Clone(rect);
            else {
                let r = this.rectLimit[this.rectLimit.length - 1];
                newrect = tt.RectangleMath.Intersect(r, rect);
            }
            this.rectLimit.push(newrect);
            this.ResetLimitRect();
        }
        PopLimitRect() {
            this.rectLimit.splice(this.rectLimit.length - 1, 1);
            this.ResetLimitRect();
        }
        ClearLimitRect() {
            this.rectLimit.splice(0, this.rectLimit.length);
            this.ResetLimitRect();
        }
        ResetLimitRect() {
            if (this.rectLimit.length == 0) {
                this._webgl.disable(this._webgl.SCISSOR_TEST);
                //this._webgl.scissor(0, 0, this.getWidth(), this.getHeight());
            }
            else {
                let rect = this.rectLimit[this.rectLimit.length - 1];
                this._webgl.enable(this._webgl.SCISSOR_TEST);
                this._webgl.scissor(rect.X, this.getHeight() - rect.Y - rect.Height, rect.Width, rect.Height);
            }
        }
    }
    tt_impl.MainScreen = MainScreen;
})(tt_impl$b || (tt_impl$b = {}));

var tt_impl$a;
(function (tt_impl) {
    (function (Shaders) {
        let ShaderType;
        (function (ShaderType) {
            ShaderType[ShaderType["VertexShader"] = 0] = "VertexShader";
            ShaderType[ShaderType["FragmentShader"] = 1] = "FragmentShader";
        })(ShaderType = Shaders.ShaderType || (Shaders.ShaderType = {}));
        class ShaderObj {
            constructor(type, name, source, shader, uniformInfo) {
                this.type = type;
                this.name = name;
                this.source = source;
                this.shader = shader;
                this.uniformInfo = uniformInfo;
            }
        }
        Shaders.ShaderObj = ShaderObj;
        var vsp = {};
        var fsp = {};
        function AddShader(webgl, type, name, source, keepsource) {
            if (type == ShaderType.VertexShader) {
                if (vsp[name] != undefined)
                    return null;
            }
            else {
                if (fsp[name] != undefined)
                    return null;
            }
            var _shader = webgl.createShader(type == ShaderType.VertexShader ? webgl.VERTEX_SHADER : webgl.FRAGMENT_SHADER);
            if (_shader == null) {
                console.error("AddShader error webgl.createShader:" + type + ":" + name);
                return null;
            }
            webgl.shaderSource(_shader, source);
            webgl.compileShader(_shader);
            var r1 = webgl.getShaderParameter(_shader, webgl.COMPILE_STATUS);
            if (r1 == false) {
                console.error("AddShader error webgl.compileShader:" + webgl.getShaderInfoLog(_shader) + ':' + type + ":" + name);
                return null;
            }
            else {
                console.log("AddShader:" + ShaderType[type].toString() + name);
            }
            var uinfo = {};
            findUniform(source, uinfo);
            var shaderobj = new ShaderObj(type, name, keepsource ? source : "", _shader, uinfo);
            if (type == ShaderType.VertexShader) {
                vsp[name] = shaderobj;
            }
            else {
                fsp[name] = shaderobj;
            }
            return shaderobj;
        }
        Shaders.AddShader = AddShader;
        function findUniform(source, target) {
            var lines = source.split("\n");
            for (var i = 0; i < lines.length; i++) {
                var line = lines[i].replace(new RegExp('\r', "g"), "");
                line = line.replace(new RegExp('\n', "g"), "");
                line = line.replace(new RegExp('\t', "g"), " ");
                line = line.replace(new RegExp(';', "g"), " ");
                line = line.replace("//", " ");
                var words = line.split(" ");
                var type = "";
                var name = "";
                var state = 0; //0 寻找uniform //1寻找type //2 寻找name
                for (var j = 0; j < words.length; j++) {
                    var word = words[j];
                    word.charCodeAt(0);
                    if (word == "")
                        continue;
                    if (word == " ")
                        continue;
                    else if (state == 0) {
                        if (word == "uniform") {
                            state++;
                        }
                        else {
                            break;
                        }
                    }
                    else if (state == 1) {
                        type = word;
                        state++;
                    }
                    else if (state == 2) {
                        name = word;
                        break;
                    }
                }
                if (type != "") {
                    if (name == "matrix" || name == "texpalette" || name == "texmain" || name == "texlut")
                        continue;
                    target[name] = type;
                }
            }
        }
        function LinkShader(webgl, name, vs, fs) {
            var prog = webgl.createProgram();
            if (prog != null) {
                webgl.attachShader(prog, vs.shader);
                webgl.attachShader(prog, fs.shader);
                webgl.linkProgram(prog);
                var r3 = webgl.getProgramParameter(prog, webgl.LINK_STATUS);
                if (r3 == false) {
                    console.error("LinkShader error webgl.linkProgram:" + webgl.getProgramInfoLog(prog));
                    return null;
                }
                var _prog = new ShaderProgram(webgl, name, prog, vs, fs);
                programs[name] = _prog;
                console.log("LinkShader:" + name);
                return _prog;
            }
            else {
                console.error("LinkShader error webgl.createProgram");
            }
            return null;
        }
        Shaders.LinkShader = LinkShader;
        var programs = {};
        class ShaderProgram {
            constructor(webgl, name, program, vs, fs) {
                this.name = name;
                this.program = program;
                this.attrIndexPos = webgl.getAttribLocation(this.program, "position");
                this.attrIndexColor = webgl.getAttribLocation(this.program, "color");
                this.attrIndexUV = webgl.getAttribLocation(this.program, "uv");
                this.attrIndexExt = webgl.getAttribLocation(this.program, "ext");
                this.uniMatModel = webgl.getUniformLocation(this.program, "matModel");
                this.uniMatView = webgl.getUniformLocation(this.program, "matView");
                this.uniMatProj = webgl.getUniformLocation(this.program, "matProj");
                this.uniTex = webgl.getUniformLocation(this.program, "tex");
                this.uniTex2 = webgl.getUniformLocation(this.program, "tex2");
                this.uniTexPal = webgl.getUniformLocation(this.program, "texpal");
                this.uniforms = {};
                for (var key in vs.uniformInfo) {
                    if (this.uniforms[key] != undefined)
                        continue;
                    //if (key == "matrix" || key == "texpalette" || key == "texmain") continue;
                    var loc = webgl.getUniformLocation(this.program, key);
                    if (loc != null) {
                        this.uniforms[key] = loc;
                    }
                }
                for (var key in fs.uniformInfo) {
                    if (this.uniforms[key] != undefined)
                        continue;
                    //if (key == "mattrix" || key == "texpalette" || key == "texmain") continue;
                    var loc = webgl.getUniformLocation(this.program, key);
                    if (loc != null) {
                        this.uniforms[key] = loc;
                    }
                }
            }
        }
        Shaders.ShaderProgram = ShaderProgram;
        function GetShaderProgram(name) {
            //if (programs[name] == undefined) return null;
            return programs[name];
        }
        Shaders.GetShaderProgram = GetShaderProgram;
    })(tt_impl.Shaders || (tt_impl.Shaders = {}));
})(tt_impl$a || (tt_impl$a = {}));

var tt_impl$9;
(function (tt_impl) {
    (function (Shaders) {
        var vs_default = `#version 300 es
    layout(location = 0) in vec3 position;//顶点提供的数据
    layout(location = 1) in vec4 color; 
    layout(location = 2) in vec2 uv; 
    layout(location = 3) in vec4 ext; 

    uniform mat4 matModel;
    uniform mat4 matView;
    uniform mat4 matProj;

    out vec4 vColor;//输出给fs的参数
    out vec2 vUv;//输出给fs的参数2
    flat out vec4 vExt;

    void main(void) 
    {
        mat4 matrix = matProj*matView*matModel;
        gl_Position = matrix * vec4(position,1);// uViewProjMatrix * uModelMatrix * position;
        vColor = color;
        vUv = uv;
        vExt = ext;
    }
    `;
        var fs_default = `#version 300 es
    precision mediump float;//指定浮点型精确度
    
    in vec2 vUv;//从vs接收的参数
    in vec4 vColor;//从vs接收的参数
    flat in vec4 vExt;

        
    layout(location = 0) out vec4 fragColor;

    //uniform sampler2D tex2;
    uniform sampler2D tex;  //从外部设置的参数
    uniform sampler2D texpal;  //调色板用
    void main(void) 
    {
        vec4 texc = texture(tex,vUv);
        vec4 outc = vColor;
        int effect = int(vExt.w);
        if(effect==0)//rgba model 
        {
            outc = vColor * texc;
        }
        else if(effect==1)//gray model
        {
            vec4 c = vec4(texc.r,texc.r,texc.r,1);
            outc = vColor * c;
        }
        else if(effect==2)//pal8 model
        {
            vec2 paluv = vec2(texc.r+vExt.x/256.0,vExt.y/256.0);
            vec4 c = texture(texpal,paluv);
            outc = vColor * c;
        }
        else if(effect==3)//p5a3 model
        {
      
            int v =  int(texc.r *255.0+0.9);
            
            //分解调色板和alpha

            float palx = float(v % 32)/255.0 + vExt.x/255.0;
            float paly = vExt.y/255.0;
            float alpha = float(v/32) /7.0f;

            vec4 c = texture(texpal,vec2(palx,paly));
            c.a = alpha;
            //c =vec4(1.0,1.0,1.0,1.0);
            outc = vColor * c;
          
        }
        else if(effect==4)//gray as alpha model
        {
            outc.a *= texc.r;
            //vec4 c = vec4(1.0,1.0,1.0,texc.r);
            //outc = vColor * c;
        }
        fragColor =  outc;
    }
    `;
        function InitShader(webgl) {
            var vsdef = tt_impl$a.Shaders.AddShader(webgl, tt_impl$a.Shaders.ShaderType.VertexShader, "default", vs_default, true);
            var fsdef = tt_impl$a.Shaders.AddShader(webgl, tt_impl$a.Shaders.ShaderType.FragmentShader, "default", fs_default, true);
            if (vsdef != null && fsdef != null)
                tt_impl$a.Shaders.LinkShader(webgl, "default", vsdef, fsdef);
            // var fsdef_noa = Shaders.AddShader(webgl, Shaders.ShaderType.FragmentShader, "default_noa", fs_default_noa, true);
            // if (vsdef != null && fsdef_noa != null)
            //     Shaders.LinkShader(webgl, "default_noa", vsdef, fsdef_noa);
            // var fspal = Shaders.AddShader(webgl, Shaders.ShaderType.FragmentShader, "palette", fs_palette, true);
            // if (vsdef != null && fspal != null)
            // Shaders.LinkShader(webgl, "palette", vsdef, fspal);
            // var fslight = Shaders.AddShader(webgl, Shaders.ShaderType.FragmentShader, "lightmap", fs_lightmap, true);
            // if (vsdef != null && fslight != null)
            // Shaders.LinkShader(webgl, "lightmap", vsdef, fslight);
        }
        Shaders.InitShader = InitShader;
    })(tt_impl.Shaders || (tt_impl.Shaders = {}));
})(tt_impl$9 || (tt_impl$9 = {}));

var tt_impl$8;
(function (tt_impl) {
    class Texture {
        constructor(webgl, width, height, format, data, isstatic, nobuf) {
            this._webgl = webgl;
            this._format = format;
            this._texobj = webgl.createTexture();
            this._id = Texture.texid;
            this._width = width;
            this._height = height;
            Texture.texid++;
            this._webgl.bindTexture(this._webgl.TEXTURE_2D, this._texobj);
            this._webgl.pixelStorei(this._webgl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
            this._webgl.pixelStorei(this._webgl.UNPACK_FLIP_Y_WEBGL, 0);
            this._webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_MIN_FILTER, this._webgl.NEAREST);
            this._webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_MAG_FILTER, this._webgl.NEAREST);
            this._webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_WRAP_S, this._webgl.CLAMP_TO_EDGE);
            this._webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_WRAP_T, this._webgl.CLAMP_TO_EDGE);
            var formatGL = format == tt.TextureFormat.RGBA32 ? this._webgl.RGBA : this._webgl.LUMINANCE;
            var type = this._webgl.UNSIGNED_BYTE;
            if (nobuf)
                return;
            if (isstatic && data == null)
                throw new Error("static texture must with a initdata.");
            this._webgl.bindTexture(this._webgl.TEXTURE_2D, this._texobj);
            if (data == null) {
                this._static = false;
                this._webgl.texImage2D(this._webgl.TEXTURE_2D, 0, formatGL, width, height, 0, formatGL, type, null);
                let bitsize = format == tt.TextureFormat.RGBA32 ? 4 : 1;
                this._pixelbuf = new Uint8Array(this._width * this._height * bitsize);
            }
            else {
                let bitsize = format == tt.TextureFormat.RGBA32 ? 4 : 1;
                if (data.length != this._width * this._height * bitsize)
                    throw new Error("wrong texSize");
                this._webgl.texImage2D(this._webgl.TEXTURE_2D, 0, formatGL, width, height, 0, formatGL, type, data);
                this._static = isstatic;
                if (isstatic) {
                    this._pixelbuf = null;
                }
                else {
                    this._pixelbuf = new Uint8Array(this._width * this._height * bitsize);
                    for (var i = 0; i < this._pixelbuf.length; i++) {
                        this._pixelbuf[i] = data[i];
                    }
                }
            }
        }
        getID() {
            return this._id;
        }
        getFormat() {
            return this._format;
        }
        getWidth() {
            return this._width;
        }
        getHeight() {
            return this._height;
        }
        IsStatic() {
            return this._static;
        }
        IsTarget() {
            return false;
        }
        getData() {
            if (this._static)
                throw new Error("not support on static texture.");
            return this._pixelbuf;
        }
        UploadImg(img) {
            this._width = img["width"];
            this._height = img["height"];
            var formatGL = this._format == tt.TextureFormat.RGBA32 ? this._webgl.RGBA : this._webgl.LUMINANCE;
            var type = this._webgl.UNSIGNED_BYTE;
            this._webgl.bindTexture(this._webgl.TEXTURE_2D, this._texobj);
            this._webgl.texImage2D(this._webgl.TEXTURE_2D, 0, formatGL, formatGL, type, img);
        }
        UploadTexture(x, y, w, h, data) {
            if (this._static)
                throw new Error("this is a closed texture.");
            if (this._pixelbuf == null)
                throw new Error("this is a closed texture.");
            let bitsize = this._format == tt.TextureFormat.RGBA32 ? 4 : 1;
            for (var dy = y; dy < y + h; dy++) {
                for (var dx = x; dx < x + w; dx++) {
                    if (dx < this._width && dx >= 0 && dy < this._height && dy >= 0) {
                        var sy = dy - y;
                        var sx = dx - x;
                        var sindex = sy * w + sx;
                        var dindex = dy * this._width + dx;
                        if (bitsize == 4) {
                            this._pixelbuf[dindex * 4 + 0] = data[sindex * 4 + 0];
                            this._pixelbuf[dindex * 4 + 1] = data[sindex * 4 + 1];
                            this._pixelbuf[dindex * 4 + 2] = data[sindex * 4 + 2];
                            this._pixelbuf[dindex * 4 + 3] = data[sindex * 4 + 3];
                        }
                        else {
                            this._pixelbuf[dindex] = data[sindex];
                        }
                    }
                }
            }
        }
        ApplyTexture(TurnToStatic) {
            var formatGL = this._format == tt.TextureFormat.RGBA32 ? this._webgl.RGBA : this._webgl.LUMINANCE;
            var type = this._webgl.UNSIGNED_BYTE;
            this._webgl.bindTexture(this._webgl.TEXTURE_2D, this._texobj);
            this._webgl.texImage2D(this._webgl.TEXTURE_2D, 0, formatGL, this._width, this._height, 0, formatGL, 
            //最后这个type，可以管格式
            type, this._pixelbuf);
            if (TurnToStatic) {
                this._static = true;
                this._pixelbuf = null;
            }
        }
        Destory() {
            if (this._texobj != null)
                this._webgl.deleteTexture(this._texobj);
            this._texobj = null;
        }
    }
    Texture.texid = 1;
    tt_impl.Texture = Texture;
})(tt_impl$8 || (tt_impl$8 = {}));

var tt_impl$7;
(function (tt_impl) {
    class RenderTarget {
        constructor(webgl, width, height, format) {
            this.rectLimit = [];
            this._webgl = webgl;
            this._format = format;
            this._fbo = webgl.createFramebuffer();
            this._id = tt_impl$8.Texture.texid;
            this._width = width;
            this._height = height;
            this.ClearColor = tt.Color.Black;
            this._texobj = webgl.createTexture();
            webgl.bindTexture(webgl.TEXTURE_2D, this._texobj);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.NEAREST);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.NEAREST);
            webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_WRAP_S, this._webgl.CLAMP_TO_EDGE);
            webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_WRAP_T, this._webgl.CLAMP_TO_EDGE);
            var formatGL = format == tt.TextureFormat.RGBA32 ? this._webgl.RGBA : this._webgl.LUMINANCE;
            webgl.texImage2D(webgl.TEXTURE_2D, 0, formatGL, width, height, 0, formatGL, webgl.UNSIGNED_BYTE, null);
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, this._fbo);
            webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl.COLOR_ATTACHMENT0, webgl.TEXTURE_2D, this._texobj, 0);
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);
        }
        Resize(width, height) {
            let webgl = this._webgl;
            this.Destory();
            // webgl.bindFramebuffer(webgl.FRAMEBUFFER, this._fbo);
            // webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl.COLOR_ATTACHMENT0, webgl.TEXTURE_2D,
            //   null, 0);
            // webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);
            //console.log("reset rt.");
            this._width = width;
            this._height = height;
            webgl.deleteTexture(this._texobj);
            this._texobj = webgl.createTexture();
            webgl.bindTexture(webgl.TEXTURE_2D, this._texobj);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.NEAREST);
            webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.NEAREST);
            webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_WRAP_S, this._webgl.CLAMP_TO_EDGE);
            webgl.texParameteri(this._webgl.TEXTURE_2D, this._webgl.TEXTURE_WRAP_T, this._webgl.CLAMP_TO_EDGE);
            var formatGL = this._format == tt.TextureFormat.RGBA32 ? this._webgl.RGBA : this._webgl.LUMINANCE;
            webgl.texImage2D(webgl.TEXTURE_2D, 0, formatGL, width, height, 0, formatGL, webgl.UNSIGNED_BYTE, null);
            this._fbo = webgl.createFramebuffer();
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, this._fbo);
            webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl.COLOR_ATTACHMENT0, webgl.TEXTURE_2D, this._texobj, 0);
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);
            //console.log("reset rt.");
        }
        IsMainOutput() {
            return false;
        }
        Begin() {
            this._webgl.bindFramebuffer(this._webgl.FRAMEBUFFER, this._fbo);
            this._webgl.viewport(0, 0, this._width, this._height);
            this._webgl.clearColor(this.ClearColor.R, this.ClearColor.G, this.ClearColor.B, this.ClearColor.A);
            this._webgl.clear(this._webgl.COLOR_BUFFER_BIT);
            this.ResetLimitRect();
        }
        End() {
            this._webgl.flush();
            this._webgl.bindFramebuffer(this._webgl.FRAMEBUFFER, null);
        }
        getID() {
            return this._id;
        }
        getFormat() {
            return this._format;
        }
        getWidth() {
            return this._width;
        }
        getHeight() {
            return this._height;
        }
        getData() {
            throw new Error("not support.");
        }
        IsStatic() {
            return true;
        }
        IsTarget() {
            return true;
        }
        UploadTexture(x, y, w, h, data) {
            throw new Error("Method not implemented.");
        }
        ApplyTexture(TurnToStatic) {
            throw new Error("Method not implemented.");
        }
        Destory() {
            if (this._fbo != null)
                this._webgl.deleteFramebuffer(this._fbo);
            if (this._texobj != null)
                this._webgl.deleteTexture(this._texobj);
            this._fbo = null;
            this._texobj = null;
        }
        PushLimitRect(rect) {
            let newrect = null;
            if (this.rectLimit.length == 0)
                newrect = tt.RectangleMath.Clone(rect);
            else {
                let r = this.rectLimit[this.rectLimit.length - 1];
                newrect = tt.RectangleMath.Intersect(r, rect);
            }
            this.rectLimit.push(newrect);
            this.ResetLimitRect();
        }
        PopLimitRect() {
            this.rectLimit.splice(this.rectLimit.length - 1, 1);
            this.ResetLimitRect();
        }
        ClearLimitRect() {
            this.rectLimit.splice(0, this.rectLimit.length);
            this.ResetLimitRect();
        }
        ResetLimitRect() {
            if (this.rectLimit.length == 0) {
                this._webgl.disable(this._webgl.SCISSOR_TEST);
                //this._webgl.scissor(0, 0, this.getWidth(), this.getHeight());
            }
            else {
                let rect = this.rectLimit[this.rectLimit.length - 1];
                this._webgl.enable(this._webgl.SCISSOR_TEST);
                this._webgl.scissor(rect.X, rect.Y, rect.Width, rect.Height);
            }
        }
    }
    tt_impl.RenderTarget = RenderTarget;
})(tt_impl$7 || (tt_impl$7 = {}));

var tt_impl$6;
(function (tt_impl) {
    class Render_Batcher {
        constructor(webgl) {
            this._webgl = webgl;
            this._target = null;
            this._lastshader = null;
            var _vbo = webgl.createBuffer();
            this._buffer = new Uint8Array(65536 * 28);
            this._bufferView = new DataView(this._buffer.buffer, 0, this._buffer.length);
            this._pointseek = 0;
            this._lastMode = webgl.TRIANGLES;
            if (_vbo != null) {
                this._vbo = _vbo;
            }
            else {
                throw new Error("error vbo.");
            }
            var _vao = webgl.createVertexArray();
            if (_vao != null) {
                this._vao = _vao;
            }
            else {
                throw new Error("error vao.");
            }
            { //初始化 vao
                webgl.bindVertexArray(_vao);
                webgl.bindBuffer(webgl.ARRAY_BUFFER, this._vbo);
                //if (shader.attrIndexPos >= 0) {
                webgl.enableVertexAttribArray(0);
                //36 是数据步长(字节)，就是数据结构的长度
                //0 是数据偏移（字节）
                webgl.vertexAttribPointer(0, 3, webgl.FLOAT, false, 28, 0);
                //}
                //if (shader.attrIndexColor >= 0) {
                webgl.enableVertexAttribArray(1);
                webgl.vertexAttribPointer(1, 4, webgl.UNSIGNED_BYTE, true, 28, 12);
                //}
                //if (shader.attrIndexUV >= 0) {
                webgl.enableVertexAttribArray(2);
                webgl.vertexAttribPointer(2, 2, webgl.FLOAT, false, 28, 16);
                //}
                //if (shader.attrIndexExt >= 0) {
                webgl.enableVertexAttribArray(3);
                webgl.vertexAttribPointer(3, 4, webgl.UNSIGNED_BYTE, false, 28, 24);
                //}
                webgl.bindVertexArray(null);
            }
            this.LookAt = new tt.Vector2(0, 0);
            this.Scale = 1.0;
            this._lastTex = null;
            this._lastTex2 = null;
            this._lastTexPal = null;
            var shader = tt_impl$a.Shaders.GetShaderProgram("default");
            if (shader == null)
                throw new Error("error load shader.");
            this._shader = shader;
            this._modelMatrix = new Float32Array(16);
            this._viewMatrix = new Float32Array(16);
            this._projMatrix = new Float32Array(16);
        }
        DrawQuads(tex, tex2, texpal, quads, quadCount) {
            if (this._target == null)
                throw new Error("Render_Batcher Not in Begin-End.");
            if (this._lastMode != this._webgl.TRIANGLES || this._lastTex != tex || this._lastTex2 != tex2
                || this._lastTexPal != texpal
                || this._pointseek + quadCount * 6 >= 65536) {
                this._Render();
                this._ApplySingle(tex, tex2, texpal);
            }
            this._lastMode = this._webgl.TRIANGLES;
            for (var i = 0; i < quadCount; i++) {
                this._AddBuf(quads[i * 4 + 0]);
                this._AddBuf(quads[i * 4 + 1]);
                this._AddBuf(quads[i * 4 + 2]);
                this._AddBuf(quads[i * 4 + 2]);
                this._AddBuf(quads[i * 4 + 1]);
                this._AddBuf(quads[i * 4 + 3]);
            }
        }
        DrawTris(tex, tex2, texpal, tris, tricount) {
            if (this._target == null)
                throw new Error("Render_Batcher Not in Begin-End.");
            if (this._lastMode != this._webgl.TRIANGLES || this._lastTex != tex || this._lastTex2 != tex2
                || this._lastTexPal != texpal
                || this._pointseek + tricount * 3 > 65536) {
                this._Render();
                this._ApplySingle(tex, tex2, texpal);
            }
            this._lastMode = this._webgl.TRIANGLES;
            for (var i = 0; i < tricount; i++) {
                this._AddBuf(tris[i * 3 + 0]);
                this._AddBuf(tris[i * 3 + 1]);
                this._AddBuf(tris[i * 3 + 2]);
            }
        }
        DrawLines(tex, tex2, texpal, lines, linecount) {
            if (this._target == null)
                throw new Error("Render_Batcher Not in Begin-End.");
            if (this._lastMode != this._webgl.LINES || this._lastTex != tex || this._lastTex2 != tex2
                || this._lastTexPal != texpal
                || this._pointseek + linecount * 2 > 65536) {
                this._Render();
                this._ApplySingle(tex, tex2, texpal);
            }
            this._lastMode = this._webgl.LINES;
            for (var i = 0; i < linecount; i++) {
                this._AddBuf(lines[i * 2 + 0]);
                this._AddBuf(lines[i * 2 + 1]);
            }
        }
        DrawPoints(tex, tex2, texpal, points, pointcount) {
            if (this._target == null)
                throw new Error("Render_Batcher Not in Begin-End.");
            if (this._lastMode != this._webgl.POINTS || this._lastTex != tex || this._lastTex2 != tex2
                || this._lastTexPal != texpal
                || this._pointseek + pointcount * 1 > 65536) {
                this._Render();
                this._ApplySingle(tex, tex2, texpal);
            }
            this._lastMode = this._webgl.POINTS;
            for (var i = 0; i < pointcount; i++) {
                this._AddBuf(points[i]);
            }
        }
        getTarget() {
            return this._target;
        }
        getName() {
            return "Batcher";
        }
        ResetMatrix() {
            this._Render();
            this._MatDefault(this._modelMatrix);
            this._MatView(this._viewMatrix);
            this._MatProj(this._projMatrix, 0, 0);
        }
        ApplyBatch() {
            this._Render();
        }
        BeginDraw(target) {
            this._target = target;
            //update viewmatrix & modelmatrxi projmatrix
            this._MatDefault(this._modelMatrix);
            this._MatView(this._viewMatrix);
            this._MatProj(this._projMatrix, 0, 0);
            let webgl = this._webgl;
            webgl.disable(webgl.CULL_FACE);
            webgl.depthMask(false); //这是zwrite
            webgl.disable(webgl.DEPTH_TEST); //这是ztest
            webgl.enable(webgl.BLEND);
            webgl.blendEquation(webgl.FUNC_ADD);
            webgl.blendFuncSeparate(webgl.SRC_ALPHA, webgl.ONE_MINUS_SRC_ALPHA, webgl.SRC_ALPHA, webgl.ONE);
        }
        _MatView(matrix) {
            let sx = this.Scale;
            let sy = this.Scale;
            let offx = -this.LookAt.X;
            let offy = -this.LookAt.Y;
            matrix[0] = sx;
            matrix[4] = 0;
            matrix[8] = 0;
            matrix[12] = offx * sx;
            matrix[1] = 0;
            matrix[5] = sy;
            matrix[9] = 0;
            matrix[13] = offy * sy;
            matrix[2] = 0;
            matrix[6] = 0;
            matrix[10] = 1;
            matrix[14] = 0;
            matrix[3] = 0;
            matrix[7] = 0;
            matrix[11] = 0;
            matrix[15] = 1;
        }
        _MatDefault(matrix) {
            matrix[0] = 1;
            matrix[4] = 0;
            matrix[8] = 0;
            matrix[12] = 0;
            matrix[1] = 0;
            matrix[5] = 1;
            matrix[9] = 0;
            matrix[13] = 0;
            matrix[2] = 0;
            matrix[6] = 0;
            matrix[10] = 1;
            matrix[14] = 0;
            matrix[3] = 0;
            matrix[7] = 0;
            matrix[11] = 0;
            matrix[15] = 1;
        }
        _MatProj(matrix, offx, offy) {
            if (this._target == null)
                throw new Error("error target.");
            let sx = 1.0 * 2 / this._target.getWidth();
            let sy = 1 * 2 / this._target.getHeight();
            if (this._target.getID() == 0) //isMainoutput
                sy *= -1;
            matrix[0] = sx;
            matrix[4] = 0;
            matrix[8] = 0;
            matrix[12] = offx;
            matrix[1] = 0;
            matrix[5] = sy;
            matrix[9] = 0;
            matrix[13] = offy;
            matrix[2] = 0;
            matrix[6] = 0;
            matrix[10] = 1;
            matrix[14] = 0;
            matrix[3] = 0;
            matrix[7] = 0;
            matrix[11] = 0;
            matrix[15] = 1;
        }
        EndDraw() {
            this._Render();
            this._target = null;
        }
        _Render() {
            if (this._pointseek == 0)
                return;
            let webgl = this._webgl;
            let shader = this._shader;
            webgl.useProgram(shader.program);
            webgl.bindVertexArray(this._vao);
            // webgl.bindBuffer(webgl.ARRAY_BUFFER, this._vbo);
            // //if (shader.attrIndexPos >= 0) {
            // webgl.enableVertexAttribArray(0);
            // //36 是数据步长(字节)，就是数据结构的长度
            // //0 是数据偏移（字节）
            // webgl.vertexAttribPointer(0, 3, webgl.FLOAT, false, 28, 0);
            // //}
            // //if (shader.attrIndexColor >= 0) {
            // webgl.enableVertexAttribArray(1);
            // webgl.vertexAttribPointer(1, 4, webgl.UNSIGNED_BYTE, true, 28, 12);
            // //}
            // //if (shader.attrIndexUV >= 0) {
            // webgl.enableVertexAttribArray(2);
            // webgl.vertexAttribPointer(2, 2, webgl.FLOAT, false, 28, 16);
            // //}
            // //if (shader.attrIndexExt >= 0) {
            // webgl.enableVertexAttribArray(3);
            // webgl.vertexAttribPointer(3, 4, webgl.UNSIGNED_BYTE, false, 28, 24);
            // //}
            if (shader.uniMatModel != null) {
                webgl.uniformMatrix4fv(shader.uniMatModel, false, this._modelMatrix);
            }
            if (shader.uniMatView != null) {
                webgl.uniformMatrix4fv(shader.uniMatView, false, this._viewMatrix);
            }
            if (shader.uniMatProj != null) {
                webgl.uniformMatrix4fv(shader.uniMatProj, false, this._projMatrix);
            }
            if (shader.uniTex != null) {
                webgl.activeTexture(webgl.TEXTURE0);
                let tex = this._GetTexture(this._lastTex);
                webgl.bindTexture(webgl.TEXTURE_2D, tex);
                webgl.uniform1i(shader.uniTex, 0);
            }
            if (shader.uniTex2 != null && this._lastTex2 != null) {
                webgl.activeTexture(webgl.TEXTURE2);
                let tex2 = this._GetTexture(this._lastTex2);
                webgl.bindTexture(webgl.TEXTURE_2D, tex2);
                webgl.uniform1i(shader.uniTex2, 2);
            }
            if (shader.uniTexPal != null && this._lastTexPal != null) {
                webgl.activeTexture(webgl.TEXTURE3);
                let texpal = this._GetTexture(this._lastTexPal);
                webgl.bindTexture(webgl.TEXTURE_2D, texpal);
                webgl.uniform1i(shader.uniTexPal, 3);
            }
            webgl.bindBuffer(webgl.ARRAY_BUFFER, this._vbo);
            webgl.bufferData(webgl.ARRAY_BUFFER, this._buffer, webgl.DYNAMIC_DRAW, 0, this._pointseek * 28);
            //webgl.bindBuffer(webgl.ARRAY_BUFFER, this._vbo);
            webgl.drawArrays(this._lastMode, 0, this._pointseek);
            // if (this._lastMode == webgl.TRIANGLES)
            //     webgl.drawArrays(this._lastMode, 0, this._pointseek / 3);
            // if (this._lastMode == webgl.LINES)
            //     webgl.drawArrays(this._lastMode, 0, this._pointseek / 2);
            // if (this._lastMode == webgl.POINTS)
            //     webgl.drawArrays(this._lastMode, 0, this._pointseek / 1);
            this._pointseek = 0;
        }
        _AddBuf(p) {
            this._bufferView.setFloat32(this._pointseek * 28 + 0, p.x, true);
            this._bufferView.setFloat32(this._pointseek * 28 + 4, p.y, true);
            this._bufferView.setFloat32(this._pointseek * 28 + 8, p.z, true);
            this._bufferView.setUint8(this._pointseek * 28 + 12, (p.r * 255) | 0);
            this._bufferView.setUint8(this._pointseek * 28 + 13, (p.g * 255) | 0);
            this._bufferView.setUint8(this._pointseek * 28 + 14, (p.b * 255) | 0);
            this._bufferView.setUint8(this._pointseek * 28 + 15, (p.a * 255) | 0);
            this._bufferView.setFloat32(this._pointseek * 28 + 16, p.u, true);
            this._bufferView.setFloat32(this._pointseek * 28 + 20, p.v, true);
            this._bufferView.setUint8(this._pointseek * 28 + 24, p.palx);
            this._bufferView.setUint8(this._pointseek * 28 + 25, p.paly);
            this._bufferView.setUint8(this._pointseek * 28 + 26, 0); //p.texid); will be auto
            this._bufferView.setUint8(this._pointseek * 28 + 27, p.eff);
            this._pointseek++;
        }
        _GetTexture(tex) {
            if (tex == null)
                return tt.graphic.getWhiteTexture()._texobj;
            var _tex = tex._texobj;
            if (_tex == undefined)
                return tt.graphic.getWhiteTexture()._texobj;
            else
                return _tex;
            // if(tex.isTarget)
            // {
            //     return (tes as impl.RenderTarget)._texobj;
            // }
            // else
            // {
            //     return (tex as impl.Texture)._texobj;
            // }
        }
        _ApplySingle(tex, tex2, texpal) {
            this._lastTex = tex;
            this._lastTex2 = tex2;
            this._lastTexPal = texpal;
        }
    }
    tt_impl.Render_Batcher = Render_Batcher;
})(tt_impl$6 || (tt_impl$6 = {}));

var tt_impl$5;
(function (tt_impl) {
    class ReadPixelTask {
        constructor(target, onReadBack) {
            this.target = target;
            this.onReadBack = onReadBack;
        }
    }
    class ttimpl_graphics {
        constructor(webgl) {
            this._tasks = [];
            this._MainScreenScale = 1.0;
            this.OnUpdate = null;
            this.OnResize = null;
            this.OnRender = null;
            this._webgl = webgl;
            this._mainscreen = new tt_impl$b.MainScreen(webgl);
            let data = new Uint8Array(64);
            for (let i = 0; i < 64; i++)
                data[i] = 255;
            this._whitetexture = new tt_impl$8.Texture(webgl, 4, 4, tt.TextureFormat.RGBA32, data, true, false);
            tt_impl$9.Shaders.InitShader(this._webgl);
            //let info = wx.getSystemInfoSync();
            //pc 平台这几个值不是稳定的
            // this._pixelRadio = window.devicePixelRatio;
            // let c = this._webgl.canvas as HTMLCanvasElement;
            // this._pixelWidth = c.clientWidth;
            // this._pixelHeight = c.clientHeight;
        }
        getMainScreen() {
            return this._mainscreen;
        }
        getWhiteTexture() {
            return this._whitetexture;
        }
        IsSupportUploadImg() {
            return true;
        }
        CreateStaticTextureFromImage(format, src) {
            let w = src.width;
            let h = src.height;
            var t = new tt_impl$8.Texture(this._webgl, w, h, format, null, true, true);
            t.UploadImg(src);
            return t;
        }
        CreateStaticTexture(w, h, format, data) {
            return new tt_impl$8.Texture(this._webgl, w, h, format, data, true, false);
        }
        CreateDynamicTexture(w, h, format) {
            return new tt_impl$8.Texture(this._webgl, w, h, format, null, false, false);
        }
        CreateRenderTarget(w, h, format) {
            return new tt_impl$7.RenderTarget(this._webgl, w, h, format);
        }
        _OnUpdateTask() {
            if (this._tasks.length > 0) {
                let task = this._tasks.pop();
                if (task == null)
                    return;
                if (task.target.IsMainOutput()) {
                    this._webgl.bindFramebuffer(this._webgl.FRAMEBUFFER, null);
                }
                else {
                    this._webgl.bindFramebuffer(this._webgl.FRAMEBUFFER, task.target._fbo);
                }
                task.target.Begin();
                var formatGL = task.target.getFormat() == tt.TextureFormat.RGBA32 ? this._webgl.RGBA : this._webgl.LUMINANCE;
                let bitsize = task.target.getFormat() == tt.TextureFormat.RGBA32 ? 4 : 1;
                let buf = new Uint8Array(task.target.getWidth() * task.target.getHeight() * bitsize);
                this._webgl.readPixels(0, 0, task.target.getWidth(), task.target.getHeight(), formatGL, this._webgl.UNSIGNED_BYTE, buf);
                task.onReadBack(task.target.getFormat(), task.target.getWidth(), task.target.getHeight(), buf);
            }
        }
        ReadRenderTarget(target, onReadBack) {
            this._tasks.push(new ReadPixelTask(target, onReadBack));
        }
        CreateRenderer_Batcher() {
            return new tt_impl$6.Render_Batcher(this._webgl);
        }
        getMainScreenScale() {
            return this._MainScreenScale;
        }
        setMainScreenScale(v) {
            this._MainScreenScale = v;
            this.UpdateScreenSize();
        }
        UpdateScreenSize() {
            let canvas = this._webgl.canvas;
            var wantwidth = (tt.graphic.getDeviceScreenWidth() * tt.graphic.getFinalScale()) | 0;
            var wantheight = (tt.graphic.getDeviceScreenHeight() * tt.graphic.getFinalScale()) | 0;
            if (canvas.width != wantwidth || canvas.height != wantheight) {
                //console.log("radio = " + radio + " ,screenwidth=" + sw + " ,windowwidth=" + ww +",ms="+ms);
                console.log("--resize wantwidth = " + wantwidth + " ,wantheight=" + wantheight);
                canvas.width = wantwidth;
                canvas.height = wantheight;
                if (tt.graphic.OnResize != null)
                    tt.graphic.OnResize(canvas.width, canvas.height);
            }
        }
        getDeviceScreenWidth() {
            let c = this._webgl.canvas;
            return c.clientWidth;
            //return  this._pixelWidth
        }
        getDeviceScreenHeight() {
            let c = this._webgl.canvas;
            return c.clientHeight;
            //return this._pixelHeight;
        }
        getDevicePixelRadio() {
            return window.devicePixelRatio;
            //return this._pixelRadio;
        }
        getFinalScale() {
            return this.getDevicePixelRadio() / this._MainScreenScale;
        }
    }
    tt_impl.ttimpl_graphics = ttimpl_graphics;
})(tt_impl$5 || (tt_impl$5 = {}));

var __awaiter$7 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var tt_impl$4;
(function (tt_impl) {
    class Loader {
        LoadStringAsync(url) {
            return __awaiter$7(this, void 0, void 0, function* () {
                let req = yield fetch(url);
                let txt = yield req.text();
                return txt;
            });
        }
        LoadBinaryAsync(url) {
            return __awaiter$7(this, void 0, void 0, function* () {
                let req = yield fetch(url);
                let bin = yield req.arrayBuffer();
                return bin;
            });
        }
        LoadImageAsync(url) {
            // while (!comp) {
            //   await tt.sleep(1);
            // }
            //标准写法，不优雅，不着急的时候可以用更优雅的tt.sleep
            let aysnc_op = new Promise((resolve, reject) => {
                let img = new Image();
                img.src = url;
                img.onload = (e) => {
                    resolve(img);
                };
                img.onerror = (e) => {
                    img = null;
                    reject("img error:" + e.toString());
                };
            });
            return aysnc_op;
        }
        LoadImageDataAsync(name, gray) {
            return __awaiter$7(this, void 0, void 0, function* () {
                let img = yield this.LoadImageAsync(name);
                if (Loader.c2d == null) {
                    let canvas = window.document.createElement("canvas");
                    Loader.c2d = canvas.getContext("2d");
                }
                Loader.c2d.canvas.width = img.width;
                Loader.c2d.canvas.height = img.height;
                Loader.c2d.clearRect(0, 0, img.width, img.height);
                Loader.c2d.drawImage(img, 0, 0);
                let data = Loader.c2d.getImageData(0, 0, img.width, img.height);
                let imgbuf = new tt.ImageBuffer();
                imgbuf.width = data.width;
                imgbuf.height = data.height;
                if (gray)
                    imgbuf.data = new Uint8Array(data.width * data.height);
                else
                    imgbuf.data = new Uint8Array(data.width * data.height * 4);
                for (var y = 0; y < data.height; y++) {
                    for (var x = 0; x < data.width; x++) {
                        if (gray) {
                            let r = data.data[(y * data.width + x) * 4 + 0];
                            let g = data.data[(y * data.width + x) * 4 + 1];
                            let b = data.data[(y * data.width + x) * 4 + 2];
                            let grayv = (0.299 * r + 0.587 * g + 0.114 * b) | 0;
                            if (grayv < 0)
                                grayv = 0;
                            if (grayv > 255)
                                grayv = 255;
                            imgbuf.data[(y * data.width + x)] = grayv;
                        }
                        else {
                            imgbuf.data[(y * data.width + x) * 4 + 0] = data.data[(y * data.width + x) * 4 + 0];
                            imgbuf.data[(y * data.width + x) * 4 + 1] = data.data[(y * data.width + x) * 4 + 1];
                            imgbuf.data[(y * data.width + x) * 4 + 2] = data.data[(y * data.width + x) * 4 + 2];
                            imgbuf.data[(y * data.width + x) * 4 + 3] = data.data[(y * data.width + x) * 4 + 3];
                        }
                    }
                }
                return imgbuf;
            });
        }
    }
    tt_impl.Loader = Loader;
})(tt_impl$4 || (tt_impl$4 = {}));

var __awaiter$6 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var tt_impl$3;
(function (tt_impl) {
    class Sound_Impl {
        constructor(audioContext, url, array) {
            if (url != null && array == null) {
                this.loaded = false;
                this.initByUrlAsync(audioContext, url);
            }
            else if (array != null && url == null) {
                this.initByDataAsync(audioContext, array);
            }
        }
        initByUrlAsync(audioContext, url) {
            return __awaiter$6(this, void 0, void 0, function* () {
                let req = yield fetch(url);
                let buff = yield req.arrayBuffer();
                this.buffer = yield audioContext.decodeAudioData(buff);
            });
        }
        initByDataAsync(audioContext, array) {
            return __awaiter$6(this, void 0, void 0, function* () {
                this.buffer = yield audioContext.decodeAudioData(array);
                this.loaded = true;
            });
        }
    }
    class BMG_Impl {
        constructor(audioContext, url) {
            let bgmaudio = new HTMLAudioElement();
            this.url = bgmaudio.src = url;
            bgmaudio.loop = true;
            this.node = audioContext.createMediaElementSource(bgmaudio);
            this.loaded = false;
            bgmaudio.onload = () => {
                this.loaded = true;
            };
            bgmaudio.loop = true;
            bgmaudio.onended = () => {
                if (this.onEnd != null)
                    this.onEnd(this);
            };
        }
        setLoop(loop) {
            this.node.mediaElement.loop = loop;
        }
        getLoop() {
            return this.node.mediaElement.loop;
        }
    }
    class Channel_Impl {
        constructor(audioContext) {
            this.sourceBGM = null;
            this.inBGM = false;
            this.audioContext = audioContext;
            this.vol = this.audioContext.createGain();
            this.vol.gain.value = 1.0;
            this.vol.connect(this.audioContext.destination);
        }
        SetBGM(bgm) {
            if (this.inBGM) {
                this.inBGM = false;
                this.sourceBGM.disconnect();
                this.sourceBGM = null;
            }
            if (bgm != null) {
                this.sourceBGM = bgm.node;
                this.sourceBGM.connect(this.vol);
            }
        }
        setVolume(v) {
            this.vol.gain.value = v;
        }
        getVolume() {
            return this.vol.gain.value;
        }
        PlaySound(sound) {
            let node = this.audioContext.createBufferSource();
            node.buffer = sound.buffer;
            node.connect(this.vol);
            node.loop = false;
            node.start();
            if (sound.onEnd != null) {
                node.onended = () => {
                    sound.onEnd(sound);
                };
            }
        }
    }
    tt_impl.Channel_Impl = Channel_Impl;
    class AudioImpl {
        constructor() {
            this.channels = [];
            // this.audioBGM = wx.createInnerAudioContext({ "useWebAudioImplement": false });
            try {
                //var _AudioContext = window["AudioContext"] || window as any ["webkitAudioContext"] || window["mozAudioContext"] || window["msAudioContext"];
                this.audioContext = new AudioContext();
                console.log("audio Context inited");
            }
            catch (e) {
                console.error("!Your browser does not support AudioContext");
                throw new Error("!Your browser does not support AudioContext");
            }
            this.Init();
        }
        GetChannel(id) {
            if (id > 16 || id < 0) {
                throw new Error("error channel id.");
            }
            while (this.channels.length <= id) {
                this.channels.push(null);
            }
            if (this.channels[id] == null) {
                this.channels[id] = new Channel_Impl(this.audioContext);
            }
            return this.channels[id];
        }
        CreateBGM(url) {
            return new BMG_Impl(this.audioContext, url);
        }
        CreateSound(url) {
            return new Sound_Impl(this.audioContext, url, null);
        }
        CreateSoundFromArrayBuffer(array) {
            return new Sound_Impl(this.audioContext, null, array);
        }
        //audioBGM: WechatMinigame.InnerAudioContext;
        Init() {
            // create empty buffer
            if (this.audioContext != null) {
                //产生一个空声音播放一下
                var buffer = this.audioContext.createBuffer(1, 1, 22050);
                var source = this.audioContext.createBufferSource();
                source.buffer = buffer;
                // connect to output (your speakers)
                source.connect(this.audioContext.destination);
                // play the file
                source.start();
            }
        }
        ReInit() {
            this.Init();
        }
    }
    tt_impl.AudioImpl = AudioImpl;
})(tt_impl$3 || (tt_impl$3 = {}));

var __awaiter$5 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var tt_impl$2;
(function (tt_impl) {
    class Input {
        constructor(canvas) {
            this._mousepress = false;
            this._pressKeys = [];
            this.points = [];
            this.ishow = false;
            this.Start(canvas);
            // wx.onKeyboardConfirm((x) => {
            //     this.onkeycomfirm(x.value);
            // });
        }
        Start(canvas) {
            window.addEventListener("keydown", (r) => {
                this.UpdateKey(r.code, true);
            });
            window.addEventListener("keyup", (r) => {
                this.UpdateKey(r.code, false);
            });
            canvas.addEventListener("mousedown", (r) => {
                if (r.button == 0) {
                    this.UpdateMouse(r, true, false);
                    this._mousepress = true;
                }
            });
            canvas.addEventListener("mouseup", (r) => {
                if (r.button == 0) {
                    this.UpdateMouse(r, false, false);
                    this._mousepress = false;
                }
            });
            canvas.addEventListener("mousemove", (r) => {
                if (r.button == 0 && this._mousepress) {
                    this.UpdateMouse(r, true, true);
                }
            });
            canvas.addEventListener("touchstart", (r) => {
                for (var i = 0; i < r.changedTouches.length; i++) {
                    this.UpdatePoint(r.changedTouches[i], true, false);
                }
            });
            canvas.addEventListener("touchmove", (r) => {
                for (var i = 0; i < r.changedTouches.length; i++) {
                    this.UpdatePoint(r.changedTouches[i], true, true);
                }
            });
            canvas.addEventListener("touchend", (r) => {
                for (var i = 0; i < r.changedTouches.length; i++) {
                    this.UpdatePoint(r.changedTouches[i], false, false);
                }
            });
            canvas.addEventListener("touchcancel", (r) => {
                for (var i = 0; i < r.changedTouches.length; i++) {
                    this.UpdatePoint(r.changedTouches[i], false, false);
                }
            });
        }
        UpdateMouse(m, press, move) {
            while (this.points.length <= 0) {
                let p = new tt.InputPoint();
                p.id = this.points.length;
                p.press = false;
                p.x = 0;
                p.y = 0;
                this.points.push(p);
            }
            this.points[0].id = 0;
            this.points[0].press = press;
            this.points[0].x = m.x;
            this.points[0].y = m.y;
            if (this.OnPoint != null)
                this.OnPoint(0, m.x, m.y, press, move);
        }
        UpdatePoint(t, press, move) {
            let touchid = t.identifier + 1;
            while (this.points.length <= touchid) {
                let p = new tt.InputPoint();
                p.id = this.points.length;
                p.press = false;
                p.x = 0;
                p.y = 0;
                this.points.push(p);
            }
            this.points[touchid].press = press;
            this.points[touchid].x = t.clientX;
            this.points[touchid].y = t.clientY;
            if (this.OnPoint != null)
                this.OnPoint(touchid, t.clientX, t.clientY, press, move);
        }
        UpdateKey(keycode, press) {
            if (press) {
                if (this._pressKeys.indexOf(keycode) >= 0)
                    return;
                else
                    this._pressKeys.push(keycode);
            }
            else {
                let i = this._pressKeys.indexOf(keycode);
                if (i >= 0) {
                    this._pressKeys.splice(i, 1);
                }
            }
            console.log("Input Key:" + keycode + "=" + press);
            if (this.OnKey != null)
                this.OnKey(keycode, press);
        }
        GetPressKeys() {
            return this._pressKeys;
        }
        IsKeyDown(keycode) {
            return this._pressKeys.indexOf(keycode) >= 0;
        }
        GetInputPoints() {
            return this.points;
        }
        Prompt(deftxt = "", maxlen = 256) {
            return __awaiter$5(this, void 0, void 0, function* () {
                //releaseinput
                for (var i = 0; i < this.points.length; i++) {
                    this.points[i].press = false;
                }
                return window.prompt("", deftxt);
            });
        }
    }
    tt_impl.Input = Input;
})(tt_impl$2 || (tt_impl$2 = {}));

var tt_impl$1;
(function (tt_impl) {
    class Platform {
        getPlatformName() {
            return "browser_" + window.navigator.userAgent;
        }
    }
    tt_impl.Platform = Platform;
})(tt_impl$1 || (tt_impl$1 = {}));

var __awaiter$4 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
class TTStore {
    constructor() {
        this._loaded = false;
        this.InitAsync();
    }
    InitAsync() {
        return __awaiter$4(this, void 0, void 0, function* () {
            yield KeyStore.OpenOrCreateDB("_ttstore_");
            this._loaded = true;
        });
    }
    IsReady() {
        return this._loaded;
    }
    GetText(key) {
        return __awaiter$4(this, void 0, void 0, function* () {
            return yield KeyStore.GetStringValue(key);
        });
    }
    GetBinary(key) {
        return __awaiter$4(this, void 0, void 0, function* () {
            return yield KeyStore.GetBinaryValue(key);
        });
    }
    SaveText(key, data) {
        return __awaiter$4(this, void 0, void 0, function* () {
            yield KeyStore.SetStringValue(key, data);
        });
    }
    SaveBinary(key, data) {
        return __awaiter$4(this, void 0, void 0, function* () {
            yield KeyStore.SetBinaryValue(key, data);
        });
    }
    SaveDataToLocal(key, data) {
        return __awaiter$4(this, void 0, void 0, function* () {
            // 创建隐藏的可下载链接
            var eleLink = document.createElement('a');
            eleLink.download = key;
            eleLink.style.display = 'none';
            // 字符内容转变成blob地址
            var blob = new Blob([data]);
            eleLink.href = URL.createObjectURL(blob);
            // 触发点击
            document.body.appendChild(eleLink);
            eleLink.click();
            // 然后移除
            document.body.removeChild(eleLink);
            return true;
        });
    }
}
//将INDEXDBSTORE 封装起来
const _storename = "__store__";
class KeyStore {
    static DeleteDb(dbname) {
        return __awaiter$4(this, void 0, void 0, function* () {
            let dbreq = indexedDB.deleteDatabase(dbname);
            let dbinit = 0;
            dbreq.onsuccess = () => {
                console.log("db delete succ.");
                dbinit = 1;
            };
            dbreq.onerror = () => {
                console.log("db delete error.");
                dbinit = 2;
            };
            while (dbinit == 0) {
                yield tt.sleep(1);
            }
        });
    }
    static OpenOrCreateDB(dbname) {
        return __awaiter$4(this, void 0, void 0, function* () {
            let dbinit = 0;
            let dbreq = indexedDB.open(dbname, 1);
            dbreq.onsuccess = (evt) => {
                console.log("db open");
                dbinit = 1;
                this.db = dbreq.result;
            };
            dbreq.onerror = () => {
                console.log(" dbreq.onerror");
                dbinit = 2;
                this.db = null;
            };
            dbreq.onupgradeneeded = (evt) => {
                console.log("db onupgradeneeded");
                this.db = dbreq.result;
                let store = this.db.createObjectStore(_storename, { "keyPath": ["_id"] });
                store.createIndex("_id", "_id", { "unique": true });
                //let idi= store.createIndex("value", "value", { "unique": false });
                //let idi2= store.createIndex("value2", "value2", { "unique": false });
                console.log(" --create store--");
                //dbinit = 1;
            };
            while (dbinit == 0) {
                yield tt.sleep(1);
            }
        });
    }
    static SetStringValue(key, data) {
        return __awaiter$4(this, void 0, void 0, function* () {
            if (this.db == null)
                return;
            let tran = this.db.transaction(_storename, "readwrite");
            let reqdone = 0;
            let store = tran.objectStore(_storename);
            let req = store.put({ "_id": key, "value": data, "format": "text" });
            req.onsuccess = (ev) => {
                reqdone = 1;
                console.log("SetValue " + " key:" + key + " data:" + JSON.stringify(data));
            };
            req.onerror = (ev) => {
                reqdone = 2;
                console.log("SetValue fail " + " key:" + key + " data:" + JSON.stringify(data));
            };
            while (reqdone == 0) {
                yield tt.sleep(1);
            }
        });
    }
    static SetBinaryValue(key, data) {
        return __awaiter$4(this, void 0, void 0, function* () {
            if (this.db == null)
                return;
            let tran = this.db.transaction(_storename, "readwrite");
            let reqdone = 0;
            let store = tran.objectStore(_storename);
            let req = store.put({ "_id": key, "value": data, "format": "hex" });
            req.onsuccess = (ev) => {
                reqdone = 1;
                console.log("SetValue " + " key:" + key + " data:" + JSON.stringify(data));
            };
            req.onerror = (ev) => {
                reqdone = 2;
                console.log("SetValue fail " + " key:" + key + " data:" + JSON.stringify(data));
            };
            while (reqdone == 0) {
                yield tt.sleep(1);
            }
        });
    }
    static GetValue(key) {
        return __awaiter$4(this, void 0, void 0, function* () {
            if (this.db == null)
                return null;
            let tran = this.db.transaction(_storename, "readonly");
            let store = tran.objectStore(_storename);
            let index = store.index("_id"); //用索引去查
            let req = index.get(key);
            //let req = store.get(key); //直接用主键查不好使
            let reqdone = 0;
            req.onsuccess = (ev) => {
                reqdone = 1;
                console.log("GetValue " + "key:" + key + " data:" + JSON.stringify(req.result));
            };
            req.onerror = (ev) => {
                reqdone = 2;
                console.log("GetValue Fail " + "key:" + JSON.stringify(req.result));
            };
            while (reqdone == 0) {
                yield tt.sleep(1);
            }
            return req.result;
        });
    }
    static GetBinaryValue(key) {
        return __awaiter$4(this, void 0, void 0, function* () {
            let v = yield this.GetValue(key);
            if (v == undefined)
                return null;
            else if (v.format == "hex")
                return v.value;
            else
                throw new Error("format is text.");
        });
    }
    static GetStringValue(key) {
        return __awaiter$4(this, void 0, void 0, function* () {
            let v = yield this.GetValue(key);
            if (v == undefined)
                return null;
            else if (v.format == "text")
                return v.value;
            else
                throw new Error("format is text.");
        });
    }
}
KeyStore.db = null;

var tt_impl;
(function (tt_impl) {
    class ttimpl_browser {
        constructor() {
            // public async LoadDirect(rootPack:tt.IPackGroup )
            // {
            //     tt.rootPack =rootPack;
            //     this.Start();
            // }
            // public async LoadResPack(url: string): Promise<void> {
            //     if (url.includes(".ttpak")) {
            //         let down = await fetch(url);
            //         let u8 = new Uint8Array(await down.arrayBuffer());
            //         tt.rootPack = new ttimpl_PackGroup_Mem("root", u8);
            //     }
            //     else {
            //         var folder = new ttimpl_PackGroup_Path(url);
            //         await folder.LoadAsync();
            //         tt.rootPack = folder;
            //     }
            //     this.Start();
            // }
            // Start():void
            // {
            //     var allres = tt.rootPack.GetAllResourceName();
            //     for (var i in allres) {
            //         console.log("res=" + allres[i]);
            //     }
            //     var jsonitem = tt.rootPack.GetPackItem("index");
            //     if (jsonitem == null)
            //         throw new Error("not have index.");
            //     var json = JSON.parse(jsonitem.GetAsText());
            //     var name = json["name"];
            //     var desc = json["desc"];
            //     var cover = json["cover"];
            //     console.warn("pack load:" + name + "  desc=" + desc + "  cover=" + cover);
            //     var scripts = json["script"] as string[];
            //     for (var s = 0; s < scripts.length; s++) {
            //         var scriptitem = tt.rootPack.GetPackItem(PathTool.GetFileName(scripts[s]).name)
            //         if (scriptitem == null)
            //             throw new Error("not have script:" + scripts[s]);
            //         eval(scriptitem.GetAsText());
            //     }
            // }
            this.timerMs = 0;
        }
        Init(canvas) {
            //let canvas = wx.createCanvas();
            //wx.createImage();
            this.webgl = canvas.getContext("webgl2", { antialias: false });
            if (this.webgl != null) {
                tt.graphic = new tt_impl$5.ttimpl_graphics(this.webgl);
                this.timerMs = new Date().getTime();
                requestAnimationFrame(this.Update.bind(this));
            }
            else {
                console.error("init webgl error.");
            }
            tt.loader = new tt_impl$4.Loader();
            tt.audio = new tt_impl$3.AudioImpl();
            tt.input = new tt_impl$2.Input(canvas);
            tt.platform = new tt_impl$1.Platform();
            tt.store = new TTStore();
        }
        ReInitAudio() {
            // (tt.audio as ttimpl_audio).Init();
        }
        SendEnvEventToUser(name, obj) {
            //     (tt.platform as tt.impl.ttimpl_platform).SendEnvEventToUser(name, obj);
        }
        Update() {
            if (this.webgl == null)
                return;
            //resize logic
            var canvas = this.webgl.canvas;
            var wantwidth = (tt.graphic.getDeviceScreenWidth() * tt.graphic.getFinalScale()) | 0;
            var wantheight = (tt.graphic.getDeviceScreenHeight() * tt.graphic.getFinalScale()) | 0;
            //console.log("canvas.clientWidth=" + canvas.clientWidth + "wantwidth=" + wantwidth + "radio=" + radio + " mainscale=" + api.tt.graphic.getMainScreenScale());
            if (canvas.width != wantwidth || canvas.height != wantheight) {
                //console.log("radio = " + radio + " ,screenwidth=" + sw + " ,windowwidth=" + ww +",ms="+ms);
                console.log("--resize wantwidth = " + wantwidth + " ,wantheight=" + wantheight);
                canvas.width = wantwidth;
                canvas.height = wantheight;
                if (tt.graphic.OnResize != null)
                    tt.graphic.OnResize(canvas.width, canvas.height);
            }
            //update next frame.
            requestAnimationFrame(this.Update.bind(this));
            var msnow = new Date().getTime();
            var delta = (msnow - this.timerMs) / 1000.0;
            this.timerMs = msnow;
            //update logic
            if (tt.graphic.OnUpdate != null)
                tt.graphic.OnUpdate(delta);
            //render logic
            tt.graphic._OnUpdateTask();
            tt.graphic.getMainScreen().Begin();
            if (tt.graphic.OnRender != null)
                tt.graphic.OnRender();
        }
    }
    tt_impl.ttimpl_browser = ttimpl_browser;
})(tt_impl || (tt_impl = {}));

//玩家动画系统
(undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var QAni_Direction;
(function (QAni_Direction) {
    QAni_Direction[QAni_Direction["Left"] = 0] = "Left";
    QAni_Direction[QAni_Direction["Right"] = 1] = "Right";
    QAni_Direction[QAni_Direction["Up"] = 2] = "Up";
    QAni_Direction[QAni_Direction["Down"] = 3] = "Down";
})(QAni_Direction || (QAni_Direction = {}));
new tt.Vector2(0, 0);
new tt.Vector2(1, 1);

(undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};

var __awaiter$3 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
//资源管理器，只简单的把资源命名化
class QFrame_ResMgr {
    static GetDefResPath() {
        return this._respath;
    }
    static SetDefResPath(respath) {
        QFrame_ResMgr._respath = respath;
        if (respath != null && respath != "") {
            if (QFrame_ResMgr._respath[QFrame_ResMgr._respath.length - 1] != "/")
                QFrame_ResMgr._respath += "/";
        }
        else {
            QFrame_ResMgr._respath = "";
        }
    }
    static LoadStandaloneSpriteAsync(url) {
        return __awaiter$3(this, void 0, void 0, function* () {
            url = QFrame_ResMgr._respath + url;
            let name = tt.PathTool.GetFileName(url).toLowerCase();
            let s = this.allStandaloneSprite[name];
            if (s != undefined)
                return s;
            let img = yield tt.loader.LoadImageAsync(url);
            let tex = tt.graphic.CreateStaticTextureFromImage(tt.TextureFormat.RGBA32, img);
            s = new tt.Sprite(tex, null);
            this.allStandaloneSprite[name] = s;
            return s;
        });
    }
    static LoadAtlasAsync(url, ignoreCase = true) {
        return __awaiter$3(this, void 0, void 0, function* () {
            url = QFrame_ResMgr._respath + url;
            //去重
            let name = tt.PathTool.GetFileName(url).toLowerCase();
            let s = this.allAtlas[name];
            if (s != undefined)
                return s;
            //加载atlas 配置
            let json = JSON.parse(yield tt.loader.LoadStringAsync(url));
            //加载图片
            let texname = json["tex"];
            let it = url.lastIndexOf("/");
            let imgurl = tt.PathTool.GetPathName(url);
            if (it >= 0)
                imgurl = imgurl.substring(0, it) + "/" + texname;
            else
                imgurl = texname;
            // let imgurl = tt.PathTool.GetPathName(url) + "/" + texname;
            let img = yield tt.loader.LoadImageAsync(imgurl);
            let tex = tt.graphic.CreateStaticTextureFromImage(tt.TextureFormat.RGBA32, img);
            //组装atlas
            s = new tt.Atlas();
            s.tex = tex;
            s.ParseJson(json, ignoreCase);
            if (ignoreCase) {
                name = name.toLowerCase();
            }
            this.allAtlas[name] = s;
            return s;
        });
    }
}
//独立精灵，往往指向一整张图片
QFrame_ResMgr.allStandaloneSprite = {};
//图集，包含图集精灵
QFrame_ResMgr.allAtlas = {};
QFrame_ResMgr._respath = "";

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Tunable Constants
// TODO: Make this overridable by user as box2d allows it too.
/**
 * You can use this to change the length scale used by your game.
 * For example for inches you could use 39.4.
 */
const b2_lengthUnitsPerMeter = 1;
/**
 * The maximum number of vertices on a convex polygon. You cannot increase
 * this too much because b2BlockAllocator has a maximum object size.
 */
const b2_maxPolygonVertices = 8;

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
function b2Assert(condition, message) {
    if (!condition)
        throw new Error(message);
}
const b2_epsilon = 1e-5; // FLT_EPSILON instead of Number.MIN_VALUE;
const b2_epsilon_sq = b2_epsilon * b2_epsilon;
// Global tuning constants based on meters-kilograms-seconds (MKS) units.
// Collision
/**
 * The maximum number of contact points between two convex shapes. Do
 * not change this value.
 */
const b2_maxManifoldPoints = 2;
/**
 * A small length used as a collision and constraint tolerance. Usually it is
 * chosen to be numerically significant, but visually insignificant. In meters.
 */
const b2_linearSlop = 0.005 * b2_lengthUnitsPerMeter;
/**
 * The radius of the polygon/edge shape skin. This should not be modified. Making
 * this smaller means polygons will have an insufficient buffer for continuous collision.
 * Making it larger may create artifacts for vertex collision.
 */
const b2_polygonRadius = 2 * b2_linearSlop;
/**
 * The maximum linear position correction used when solving constraints. This helps to
 * prevent overshoot. Meters.
 */
const b2_maxLinearCorrection = 0.2 * b2_lengthUnitsPerMeter;
/**
 * This scale factor controls how fast overlap is resolved. Ideally this would be 1 so
 * that overlap is removed in one time step. However using values close to 1 often lead
 * to overshoot.
 */
const b2_baumgarte = 0.2;
const b2_toiBaumgarte = 0.75;
function b2MakeNumberArray(length, init = 0) {
    const result = new Array(length);
    for (let i = 0; i < length; i++)
        result[i] = init;
    return result;
}
function b2MakeArray(length, Class) {
    const result = new Array(length);
    for (let i = 0; i < length; i++)
        result[i] = new Class();
    return result;
}

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "./b2_common";
const b2_two_pi = 2 * Math.PI;
function b2Clamp(a, low, high) {
    if (a < low)
        return low;
    return a > high ? high : a;
}
/**
 * A 2D column vector.
 */
class b2Vec2 {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }
    Clone() {
        return new b2Vec2(this.x, this.y);
    }
    /**
     * Set this vector to all zeros.
     */
    SetZero() {
        this.x = 0;
        this.y = 0;
        return this;
    }
    /**
     * Set this vector to some specified coordinates.
     */
    Set(x, y) {
        this.x = x;
        this.y = y;
        return this;
    }
    Copy(other) {
        this.x = other.x;
        this.y = other.y;
        return this;
    }
    /**
     * Add a vector to this vector.
     */
    Add(v) {
        this.x += v.x;
        this.y += v.y;
        return this;
    }
    /**
     * Add a vector to this vector.
     */
    AddXY(x, y) {
        this.x += x;
        this.y += y;
        return this;
    }
    /**
     * Subtract a vector from this vector.
     */
    Subtract(v) {
        this.x -= v.x;
        this.y -= v.y;
        return this;
    }
    /**
     * Subtract a vector from this vector.
     */
    SubtractXY(x, y) {
        this.x -= x;
        this.y -= y;
        return this;
    }
    /**
     * Multiply this vector by a scalar.
     */
    Scale(s) {
        this.x *= s;
        this.y *= s;
        return this;
    }
    AddScaled(s, v) {
        this.x += s * v.x;
        this.y += s * v.y;
        return this;
    }
    SubtractScaled(s, v) {
        this.x -= s * v.x;
        this.y -= s * v.y;
        return this;
    }
    /**
     * Perform the dot product on two vectors.
     */
    Dot(v) {
        return this.x * v.x + this.y * v.y;
    }
    /**
     * Perform the cross product on two vectors. In 2D this produces a scalar.
     */
    Cross(v) {
        return this.x * v.y - this.y * v.x;
    }
    /**
     * Get the length of this vector (the norm).
     */
    Length() {
        const { x, y } = this;
        return Math.sqrt(x * x + y * y);
    }
    /**
     * Get the length squared. For performance, use this instead of
     * b2Vec2::Length (if possible).
     */
    LengthSquared() {
        const { x, y } = this;
        return x * x + y * y;
    }
    /**
     * Convert this vector into a unit vector. Returns the length.
     */
    Normalize() {
        const length = this.Length();
        if (length < b2_epsilon) {
            return 0;
        }
        const inv_length = 1 / length;
        this.x *= inv_length;
        this.y *= inv_length;
        return length;
    }
    Rotate(radians) {
        const c = Math.cos(radians);
        const s = Math.sin(radians);
        const { x } = this;
        this.x = c * x - s * this.y;
        this.y = s * x + c * this.y;
        return this;
    }
    RotateCosSin(c, s) {
        const { x } = this;
        this.x = c * x - s * this.y;
        this.y = s * x + c * this.y;
        return this;
    }
    /**
     * Does this vector contain finite coordinates?
     */
    IsValid() {
        return Number.isFinite(this.x) && Number.isFinite(this.y);
    }
    Abs() {
        this.x = Math.abs(this.x);
        this.y = Math.abs(this.y);
        return this;
    }
    GetAbs(out) {
        out.x = Math.abs(this.x);
        out.y = Math.abs(this.y);
        return out;
    }
    /**
     * Negate this vector.
     */
    Negate() {
        this.x = -this.x;
        this.y = -this.y;
        return this;
    }
    /**
     * Skew this vector such that dot(skew_vec, other) == cross(vec, other)
     */
    Skew() {
        const { x } = this;
        this.x = -this.y;
        this.y = x;
        return this;
    }
    static Min(a, b, out) {
        out.x = Math.min(a.x, b.x);
        out.y = Math.min(a.y, b.y);
        return out;
    }
    static Max(a, b, out) {
        out.x = Math.max(a.x, b.x);
        out.y = Math.max(a.y, b.y);
        return out;
    }
    static Clamp(v, lo, hi, out) {
        out.x = b2Clamp(v.x, lo.x, hi.x);
        out.y = b2Clamp(v.y, lo.y, hi.y);
        return out;
    }
    static Rotate(v, radians, out) {
        const v_x = v.x;
        const v_y = v.y;
        const c = Math.cos(radians);
        const s = Math.sin(radians);
        out.x = c * v_x - s * v_y;
        out.y = s * v_x + c * v_y;
        return out;
    }
    static Dot(a, b) {
        return a.x * b.x + a.y * b.y;
    }
    static Cross(a, b) {
        return a.x * b.y - a.y * b.x;
    }
    /**
     * Perform the cross product on a vector and a scalar. In 2D this produces
     * a vector.
     */
    static CrossVec2Scalar(v, s, out) {
        const v_x = v.x;
        out.x = s * v.y;
        out.y = -s * v_x;
        return out;
    }
    static CrossVec2One(v, out) {
        const v_x = v.x;
        out.x = v.y;
        out.y = -v_x;
        return out;
    }
    /**
     * Perform the cross product on a scalar and a vector. In 2D this produces
     * a vector.
     */
    static CrossScalarVec2(s, v, out) {
        const v_x = v.x;
        out.x = -s * v.y;
        out.y = s * v_x;
        return out;
    }
    static CrossOneVec2(v, out) {
        const v_x = v.x;
        out.x = -v.y;
        out.y = v_x;
        return out;
    }
    /**
     * Add two vectors component-wise.
     */
    static Add(a, b, out) {
        out.x = a.x + b.x;
        out.y = a.y + b.y;
        return out;
    }
    /**
     * Subtract two vectors component-wise.
     */
    static Subtract(a, b, out) {
        out.x = a.x - b.x;
        out.y = a.y - b.y;
        return out;
    }
    static Scale(s, v, out) {
        out.x = v.x * s;
        out.y = v.y * s;
        return out;
    }
    static AddScaled(a, s, b, out) {
        out.x = a.x + s * b.x;
        out.y = a.y + s * b.y;
        return out;
    }
    static SubtractScaled(a, s, b, out) {
        out.x = a.x - s * b.x;
        out.y = a.y - s * b.y;
        return out;
    }
    static AddCrossScalarVec2(a, s, v, out) {
        const v_x = v.x;
        out.x = a.x - s * v.y;
        out.y = a.y + s * v_x;
        return out;
    }
    static Mid(a, b, out) {
        out.x = (a.x + b.x) * 0.5;
        out.y = (a.y + b.y) * 0.5;
        return out;
    }
    static Extents(a, b, out) {
        out.x = (b.x - a.x) * 0.5;
        out.y = (b.y - a.y) * 0.5;
        return out;
    }
    static Equals(a, b) {
        return a.x === b.x && a.y === b.y;
    }
    static Distance(a, b) {
        return Math.sqrt(Math.pow((a.x - b.x), 2) + Math.pow((a.y - b.y), 2));
    }
    static DistanceSquared(a, b) {
        return Math.pow((a.x - b.x), 2) + Math.pow((a.y - b.y), 2);
    }
    /**
     * Negate a vector.
     */
    static Negate(v, out) {
        out.x = -v.x;
        out.y = -v.y;
        return out;
    }
    static Normalize(v, out) {
        const length_sq = Math.pow(v.x, 2) + Math.pow(v.y, 2);
        if (length_sq >= b2_epsilon_sq) {
            const inv_length = 1 / Math.sqrt(length_sq);
            out.x = inv_length * v.x;
            out.y = inv_length * v.y;
        }
        else {
            out.x = 0;
            out.y = 0;
        }
        return out;
    }
    /**
     * Skew a vector such that dot(skew_vec, other) == cross(vec, other)
     */
    static Skew(v, out) {
        const { x } = v;
        out.x = -v.y;
        out.y = x;
        return out;
    }
}
b2Vec2.ZERO = new b2Vec2();
b2Vec2.UNITX = new b2Vec2(1, 0);
b2Vec2.UNITY = new b2Vec2(0, 1);
b2Vec2.s_t0 = new b2Vec2();
b2Vec2.s_t1 = new b2Vec2();
b2Vec2.s_t2 = new b2Vec2();
b2Vec2.s_t3 = new b2Vec2();
/**
 * A 2-by-2 matrix. Stored in column-major order.
 */
class b2Mat22 {
    constructor() {
        this.ex = new b2Vec2(1, 0);
        this.ey = new b2Vec2(0, 1);
    }
    Clone() {
        return new b2Mat22().Copy(this);
    }
    /**
     * Construct a matrix using columns.
     */
    static FromColumns(c1, c2) {
        return new b2Mat22().SetColumns(c1, c2);
    }
    /**
     * Construct a matrix using scalars.
     */
    static FromScalars(r1c1, r1c2, r2c1, r2c2) {
        return new b2Mat22().SetScalars(r1c1, r1c2, r2c1, r2c2);
    }
    static FromAngle(radians) {
        return new b2Mat22().SetAngle(radians);
    }
    /**
     * Set this matrix using scalars.
     */
    SetScalars(r1c1, r1c2, r2c1, r2c2) {
        this.ex.Set(r1c1, r2c1);
        this.ey.Set(r1c2, r2c2);
        return this;
    }
    /**
     * Initialize this matrix using columns.
     */
    SetColumns(c1, c2) {
        this.ex.Copy(c1);
        this.ey.Copy(c2);
        return this;
    }
    SetAngle(radians) {
        const c = Math.cos(radians);
        const s = Math.sin(radians);
        this.ex.Set(c, s);
        this.ey.Set(-s, c);
        return this;
    }
    Copy(other) {
        this.ex.Copy(other.ex);
        this.ey.Copy(other.ey);
        return this;
    }
    /**
     * Set this to the identity matrix.
     */
    SetIdentity() {
        this.ex.Set(1, 0);
        this.ey.Set(0, 1);
        return this;
    }
    /**
     * Set this matrix to all zeros.
     */
    SetZero() {
        this.ex.SetZero();
        this.ey.SetZero();
        return this;
    }
    GetAngle() {
        return Math.atan2(this.ex.y, this.ex.x);
    }
    /**
     * Solve A * x = b, where b is a column vector. This is more efficient
     * than computing the inverse in one-shot cases.
     */
    Solve(b_x, b_y, out) {
        const a11 = this.ex.x;
        const a12 = this.ey.x;
        const a21 = this.ex.y;
        const a22 = this.ey.y;
        let det = a11 * a22 - a12 * a21;
        if (det !== 0) {
            det = 1 / det;
        }
        out.x = det * (a22 * b_x - a12 * b_y);
        out.y = det * (a11 * b_y - a21 * b_x);
        return out;
    }
    Abs() {
        this.ex.Abs();
        this.ey.Abs();
        return this;
    }
    Inverse() {
        this.GetInverse(this);
        return this;
    }
    Add(M) {
        this.ex.Add(M.ex);
        this.ey.Add(M.ey);
        return this;
    }
    Subtract(M) {
        this.ex.Subtract(M.ex);
        this.ey.Subtract(M.ey);
        return this;
    }
    GetInverse(out) {
        const a = this.ex.x;
        const b = this.ey.x;
        const c = this.ex.y;
        const d = this.ey.y;
        let det = a * d - b * c;
        if (det !== 0) {
            det = 1 / det;
        }
        out.ex.x = det * d;
        out.ey.x = -det * b;
        out.ex.y = -det * c;
        out.ey.y = det * a;
        return out;
    }
    GetAbs(out) {
        out.ex.x = Math.abs(this.ex.x);
        out.ex.y = Math.abs(this.ex.y);
        out.ey.x = Math.abs(this.ey.x);
        out.ey.y = Math.abs(this.ey.y);
        return out;
    }
    /**
     * Multiply a matrix times a vector. If a rotation matrix is provided,
     * then this transforms the vector from one frame to another.
     */
    static MultiplyVec2(M, v, out) {
        const v_x = v.x;
        const v_y = v.y;
        out.x = M.ex.x * v_x + M.ey.x * v_y;
        out.y = M.ex.y * v_x + M.ey.y * v_y;
        return out;
    }
    /**
     * Multiply a matrix transpose times a vector. If a rotation matrix is provided,
     * then this transforms the vector from one frame to another (inverse transform).
     */
    static TransposeMultiplyVec2(M, v, out) {
        const v_x = v.x;
        const v_y = v.y;
        out.x = M.ex.x * v_x + M.ex.y * v_y;
        out.y = M.ey.x * v_x + M.ey.y * v_y;
        return out;
    }
    static Add(A, B, out) {
        out.ex.x = A.ex.x + B.ex.x;
        out.ex.y = A.ex.y + B.ex.y;
        out.ey.x = A.ey.x + B.ey.x;
        out.ey.y = A.ey.y + B.ey.y;
        return out;
    }
    /** A * B */
    static Multiply(A, B, out) {
        const A_ex_x = A.ex.x;
        const A_ex_y = A.ex.y;
        const A_ey_x = A.ey.x;
        const A_ey_y = A.ey.y;
        const B_ex_x = B.ex.x;
        const B_ex_y = B.ex.y;
        const B_ey_x = B.ey.x;
        const B_ey_y = B.ey.y;
        out.ex.x = A_ex_x * B_ex_x + A_ey_x * B_ex_y;
        out.ex.y = A_ex_y * B_ex_x + A_ey_y * B_ex_y;
        out.ey.x = A_ex_x * B_ey_x + A_ey_x * B_ey_y;
        out.ey.y = A_ex_y * B_ey_x + A_ey_y * B_ey_y;
        return out;
    }
    /** A^T * B */
    static TransposeMultiply(A, B, out) {
        const A_ex_x = A.ex.x;
        const A_ex_y = A.ex.y;
        const A_ey_x = A.ey.x;
        const A_ey_y = A.ey.y;
        const B_ex_x = B.ex.x;
        const B_ex_y = B.ex.y;
        const B_ey_x = B.ey.x;
        const B_ey_y = B.ey.y;
        out.ex.x = A_ex_x * B_ex_x + A_ex_y * B_ex_y;
        out.ex.y = A_ey_x * B_ex_x + A_ey_y * B_ex_y;
        out.ey.x = A_ex_x * B_ey_x + A_ex_y * B_ey_y;
        out.ey.y = A_ey_x * B_ey_x + A_ey_y * B_ey_y;
        return out;
    }
}
b2Mat22.IDENTITY = new b2Mat22();
/**
 * Rotation
 */
class b2Rot {
    /**
     * Initialize from an angle in radians
     */
    constructor(angle = 0) {
        /** Sine */
        this.s = 0;
        /** Cosine */
        this.c = 1;
        if (angle) {
            this.s = Math.sin(angle);
            this.c = Math.cos(angle);
        }
    }
    Clone() {
        return new b2Rot().Copy(this);
    }
    Copy(other) {
        this.s = other.s;
        this.c = other.c;
        return this;
    }
    /**
     * Set using an angle in radians.
     */
    Set(angle) {
        this.s = Math.sin(angle);
        this.c = Math.cos(angle);
        return this;
    }
    /**
     * Set to the identity rotation
     */
    SetIdentity() {
        this.s = 0;
        this.c = 1;
        return this;
    }
    /**
     * Get the angle in radians
     */
    GetAngle() {
        return Math.atan2(this.s, this.c);
    }
    /**
     * Get the x-axis
     */
    GetXAxis(out) {
        out.x = this.c;
        out.y = this.s;
        return out;
    }
    /**
     * Get the u-axis
     */
    GetYAxis(out) {
        out.x = -this.s;
        out.y = this.c;
        return out;
    }
    /**
     * Multiply two rotations: q * r
     */
    static Multiply(q, r, out) {
        // [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]
        // [qs  qc]   [rs  rc]   [qs*rc+qc*rs -qs*rs+qc*rc]
        // s = qs * rc + qc * rs
        // c = qc * rc - qs * rs
        const s = q.s * r.c + q.c * r.s;
        const c = q.c * r.c - q.s * r.s;
        out.s = s;
        out.c = c;
        return out;
    }
    /**
     * Transpose multiply two rotations: qT * r
     */
    static TransposeMultiply(q, r, out) {
        // [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc]
        // [-qs qc]   [rs  rc]   [-qs*rc+qc*rs qs*rs+qc*rc]
        // s = qc * rs - qs * rc
        // c = qc * rc + qs * rs
        const s = q.c * r.s - q.s * r.c;
        const c = q.c * r.c + q.s * r.s;
        out.s = s;
        out.c = c;
        return out;
    }
    /**
     * Rotate a vector
     */
    static MultiplyVec2(q, v, out) {
        const v_x = v.x;
        const v_y = v.y;
        out.x = q.c * v_x - q.s * v_y;
        out.y = q.s * v_x + q.c * v_y;
        return out;
    }
    /**
     * Inverse rotate a vector
     */
    static TransposeMultiplyVec2(q, v, out) {
        const v_x = v.x;
        const v_y = v.y;
        out.x = q.c * v_x + q.s * v_y;
        out.y = -q.s * v_x + q.c * v_y;
        return out;
    }
}
b2Rot.IDENTITY = new b2Rot();
/**
 * A transform contains translation and rotation. It is used to represent
 * the position and orientation of rigid frames.
 */
class b2Transform {
    constructor() {
        this.p = new b2Vec2();
        this.q = new b2Rot();
    }
    Clone() {
        return new b2Transform().Copy(this);
    }
    Copy(other) {
        this.p.Copy(other.p);
        this.q.Copy(other.q);
        return this;
    }
    /**
     * Set this to the identity transform.
     */
    SetIdentity() {
        this.p.SetZero();
        this.q.SetIdentity();
        return this;
    }
    /**
     * Set this based on the position and rotation.
     */
    SetPositionRotation(position, q) {
        this.p.Copy(position);
        this.q.Copy(q);
        return this;
    }
    /**
     * Set this based on the position and angle.
     */
    SetPositionAngle(pos, a) {
        this.p.Copy(pos);
        this.q.Set(a);
        return this;
    }
    SetPosition(position) {
        this.p.Copy(position);
        return this;
    }
    SetPositionXY(x, y) {
        this.p.Set(x, y);
        return this;
    }
    SetRotation(rotation) {
        this.q.Copy(rotation);
        return this;
    }
    SetRotationAngle(radians) {
        this.q.Set(radians);
        return this;
    }
    GetPosition() {
        return this.p;
    }
    GetRotation() {
        return this.q;
    }
    GetAngle() {
        return this.q.GetAngle();
    }
    static MultiplyVec2(T, v, out) {
        const v_x = v.x;
        const v_y = v.y;
        out.x = T.q.c * v_x - T.q.s * v_y + T.p.x;
        out.y = T.q.s * v_x + T.q.c * v_y + T.p.y;
        return out;
    }
    static TransposeMultiplyVec2(T, v, out) {
        const px = v.x - T.p.x;
        const py = v.y - T.p.y;
        out.x = T.q.c * px + T.q.s * py;
        out.y = -T.q.s * px + T.q.c * py;
        return out;
    }
    /**
     * v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
     *    = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
     */
    static Multiply(A, B, out) {
        b2Rot.Multiply(A.q, B.q, out.q);
        b2Rot.MultiplyVec2(A.q, B.p, out.p).Add(A.p);
        return out;
    }
    /**
     * v2 = A.q' * (B.q * v1 + B.p - A.p)
     *    = A.q' * B.q * v1 + A.q' * (B.p - A.p)
     */
    static TransposeMultiply(A, B, out) {
        b2Rot.TransposeMultiply(A.q, B.q, out.q);
        b2Rot.TransposeMultiplyVec2(A.q, b2Vec2.Subtract(B.p, A.p, out.p), out.p);
        return out;
    }
}
b2Transform.IDENTITY = new b2Transform();
/**
 * This describes the motion of a body/shape for TOI computation.
 * Shapes are defined with respect to the body origin, which may
 * no coincide with the center of mass. However, to support dynamics
 * we must interpolate the center of mass position.
 */
class b2Sweep {
    constructor() {
        /** Local center of mass position */
        this.localCenter = new b2Vec2();
        /** Center world position at time 0 */
        this.c0 = new b2Vec2();
        /** Center world position at time 1 */
        this.c = new b2Vec2();
        /** World angle at time 0 */
        this.a0 = 0;
        /** World angle at time 1 */
        this.a = 0;
        /**
         * Fraction of the current time step in the range [0,1]
         * c0 and a0 are the positions at alpha0.
         */
        this.alpha0 = 0;
    }
    Clone() {
        return new b2Sweep().Copy(this);
    }
    Copy(other) {
        this.localCenter.Copy(other.localCenter);
        this.c0.Copy(other.c0);
        this.c.Copy(other.c);
        this.a0 = other.a0;
        this.a = other.a;
        this.alpha0 = other.alpha0;
        return this;
    }
    /**
     * Get the interpolated transform at a specific time.
     *
     * @param transform The output transform
     * @param beta Is a factor in [0,1], where 0 indicates alpha0.
     * @see https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
     */
    GetTransform(xf, beta) {
        const oneMinusBeta = 1 - beta;
        xf.p.x = oneMinusBeta * this.c0.x + beta * this.c.x;
        xf.p.y = oneMinusBeta * this.c0.y + beta * this.c.y;
        const angle = oneMinusBeta * this.a0 + beta * this.a;
        xf.q.Set(angle);
        // Shift to origin
        xf.p.Subtract(b2Rot.MultiplyVec2(xf.q, this.localCenter, b2Vec2.s_t0));
        return xf;
    }
    /**
     * Advance the sweep forward, yielding a new initial state.
     *
     * @param alpha The new initial time.
     */
    Advance(alpha) {
        // DEBUG: b2Assert(this.alpha0 < 1);
        const beta = (alpha - this.alpha0) / (1 - this.alpha0);
        this.c0.x += beta * (this.c.x - this.c0.x);
        this.c0.y += beta * (this.c.y - this.c0.y);
        this.a0 += beta * (this.a - this.a0);
        this.alpha0 = alpha;
    }
    /**
     * Normalize an angle in radians to be between -pi and pi
     */
    Normalize() {
        const d = b2_two_pi * Math.floor(this.a0 / b2_two_pi);
        this.a0 -= d;
        this.a -= d;
    }
}

// MIT License
var b2ShapeType;
(function (b2ShapeType) {
    b2ShapeType[b2ShapeType["e_unknown"] = -1] = "e_unknown";
    b2ShapeType[b2ShapeType["e_circle"] = 0] = "e_circle";
    b2ShapeType[b2ShapeType["e_edge"] = 1] = "e_edge";
    b2ShapeType[b2ShapeType["e_polygon"] = 2] = "e_polygon";
    b2ShapeType[b2ShapeType["e_chain"] = 3] = "e_chain";
    b2ShapeType[b2ShapeType["e_typeCount"] = 4] = "e_typeCount";
})(b2ShapeType || (b2ShapeType = {}));
/**
 * A shape is used for collision detection. You can create a shape however you like.
 * Shapes used for simulation in b2World are created automatically when a b2Fixture
 * is created. Shapes may encapsulate a one or more child shapes.
 */
class b2Shape {
    constructor(type, radius) {
        /**
         * Radius of a shape. For polygonal shapes this must be b2_polygonRadius. There is no support for
         * making rounded polygons.
         */
        this.m_radius = 0;
        this.m_type = type;
        this.m_radius = radius;
    }
    Copy(other) {
        // DEBUG: b2Assert(this.m_type === other.m_type);
        this.m_radius = other.m_radius;
        return this;
    }
    /**
     * Get the type of this shape. You can use this to down cast to the concrete shape.
     *
     * @returns The shape type.
     */
    GetType() {
        return this.m_type;
    }
}

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
/**
 * A distance proxy is used by the GJK algorithm.
 * It encapsulates any shape.
 */
class b2DistanceProxy {
    constructor() {
        this.m_buffer = b2MakeArray(2, b2Vec2);
        this.m_vertices = this.m_buffer;
        this.m_count = 0;
        this.m_radius = 0;
    }
    Copy(other) {
        if (other.m_vertices === other.m_buffer) {
            this.m_vertices = this.m_buffer;
            this.m_buffer[0].Copy(other.m_buffer[0]);
            this.m_buffer[1].Copy(other.m_buffer[1]);
        }
        else {
            this.m_vertices = other.m_vertices;
        }
        this.m_count = other.m_count;
        this.m_radius = other.m_radius;
        return this;
    }
    Reset() {
        this.m_vertices = this.m_buffer;
        this.m_count = 0;
        this.m_radius = 0;
        return this;
    }
    SetShape(shape, index) {
        shape.SetupDistanceProxy(this, index);
    }
    SetVerticesRadius(vertices, count, radius) {
        this.m_vertices = vertices;
        this.m_count = count;
        this.m_radius = radius;
    }
    GetSupport(d) {
        let bestIndex = 0;
        let bestValue = b2Vec2.Dot(this.m_vertices[0], d);
        for (let i = 1; i < this.m_count; ++i) {
            const value = b2Vec2.Dot(this.m_vertices[i], d);
            if (value > bestValue) {
                bestIndex = i;
                bestValue = value;
            }
        }
        return bestIndex;
    }
    GetSupportVertex(d) {
        let bestIndex = 0;
        let bestValue = b2Vec2.Dot(this.m_vertices[0], d);
        for (let i = 1; i < this.m_count; ++i) {
            const value = b2Vec2.Dot(this.m_vertices[i], d);
            if (value > bestValue) {
                bestIndex = i;
                bestValue = value;
            }
        }
        return this.m_vertices[bestIndex];
    }
    GetVertexCount() {
        return this.m_count;
    }
    GetVertex(index) {
        // DEBUG: b2Assert(0 <= index && index < this.m_count);
        return this.m_vertices[index];
    }
}
/**
 * Input for b2Distance.
 * You have to option to use the shape radii
 * in the computation. Even
 */
class b2DistanceInput {
    constructor() {
        this.proxyA = new b2DistanceProxy();
        this.proxyB = new b2DistanceProxy();
        this.transformA = new b2Transform();
        this.transformB = new b2Transform();
        this.useRadii = false;
    }
    Reset() {
        this.proxyA.Reset();
        this.proxyB.Reset();
        this.transformA.SetIdentity();
        this.transformB.SetIdentity();
        this.useRadii = false;
        return this;
    }
}

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// Structures and functions used for computing contact points, distance queries, and TOI queries.
// DEBUG: import { b2Assert } from "../common/b2_common";
var b2ContactFeatureType;
(function (b2ContactFeatureType) {
    b2ContactFeatureType[b2ContactFeatureType["e_vertex"] = 0] = "e_vertex";
    b2ContactFeatureType[b2ContactFeatureType["e_face"] = 1] = "e_face";
})(b2ContactFeatureType || (b2ContactFeatureType = {}));
/**
 * The features that intersect to form the contact point
 * This must be 4 bytes or less.
 */
class b2ContactFeature {
    constructor() {
        this.m_key = 0;
        this.m_key_invalid = false;
        /** Feature index on shapeA */
        this.m_indexA = 0;
        /** Feature index on shapeB */
        this.m_indexB = 0;
        /** The feature type on shapeA */
        this.m_typeA = b2ContactFeatureType.e_vertex;
        /** The feature type on shapeB */
        this.m_typeB = b2ContactFeatureType.e_vertex;
    }
    get key() {
        if (this.m_key_invalid) {
            this.m_key_invalid = false;
            this.m_key = this.m_indexA | (this.m_indexB << 8) | (this.m_typeA << 16) | (this.m_typeB << 24);
        }
        return this.m_key;
    }
    set key(value) {
        this.m_key = value;
        this.m_key_invalid = false;
        this.m_indexA = this.m_key & 0xff;
        this.m_indexB = (this.m_key >> 8) & 0xff;
        this.m_typeA = (this.m_key >> 16) & 0xff;
        this.m_typeB = (this.m_key >> 24) & 0xff;
    }
    get indexA() {
        return this.m_indexA;
    }
    set indexA(value) {
        this.m_indexA = value;
        this.m_key_invalid = true;
    }
    get indexB() {
        return this.m_indexB;
    }
    set indexB(value) {
        this.m_indexB = value;
        this.m_key_invalid = true;
    }
    get typeA() {
        return this.m_typeA;
    }
    set typeA(value) {
        this.m_typeA = value;
        this.m_key_invalid = true;
    }
    get typeB() {
        return this.m_typeB;
    }
    set typeB(value) {
        this.m_typeB = value;
        this.m_key_invalid = true;
    }
}
/**
 * Contact ids to facilitate warm starting.
 */
class b2ContactID {
    constructor() {
        this.cf = new b2ContactFeature();
    }
    Copy(o) {
        this.key = o.key;
        return this;
    }
    Clone() {
        return new b2ContactID().Copy(this);
    }
    get key() {
        return this.cf.key;
    }
    set key(value) {
        this.cf.key = value;
    }
}
var b2ManifoldType;
(function (b2ManifoldType) {
    b2ManifoldType[b2ManifoldType["e_circles"] = 0] = "e_circles";
    b2ManifoldType[b2ManifoldType["e_faceA"] = 1] = "e_faceA";
    b2ManifoldType[b2ManifoldType["e_faceB"] = 2] = "e_faceB";
})(b2ManifoldType || (b2ManifoldType = {}));
/**
 * This is used to compute the current state of a contact manifold.
 */
class b2WorldManifold {
    constructor() {
        /** World vector pointing from A to B */
        this.normal = new b2Vec2();
        /** World contact point (point of intersection) */
        this.points = b2MakeArray(b2_maxManifoldPoints, b2Vec2);
        /** A negative value indicates overlap, in meters */
        this.separations = b2MakeNumberArray(b2_maxManifoldPoints);
    }
    Initialize(manifold, xfA, radiusA, xfB, radiusB) {
        if (manifold.pointCount === 0) {
            return;
        }
        switch (manifold.type) {
            case b2ManifoldType.e_circles: {
                this.normal.Set(1, 0);
                const pointA = b2Transform.MultiplyVec2(xfA, manifold.localPoint, b2WorldManifold.Initialize_s_pointA);
                const pointB = b2Transform.MultiplyVec2(xfB, manifold.points[0].localPoint, b2WorldManifold.Initialize_s_pointB);
                if (b2Vec2.DistanceSquared(pointA, pointB) > b2_epsilon_sq) {
                    b2Vec2.Subtract(pointB, pointA, this.normal).Normalize();
                }
                const cA = b2Vec2.AddScaled(pointA, radiusA, this.normal, b2WorldManifold.Initialize_s_cA);
                const cB = b2Vec2.SubtractScaled(pointB, radiusB, this.normal, b2WorldManifold.Initialize_s_cB);
                b2Vec2.Mid(cA, cB, this.points[0]);
                this.separations[0] = b2Vec2.Dot(b2Vec2.Subtract(cB, cA, b2Vec2.s_t0), this.normal);
                break;
            }
            case b2ManifoldType.e_faceA: {
                b2Rot.MultiplyVec2(xfA.q, manifold.localNormal, this.normal);
                const planePoint = b2Transform.MultiplyVec2(xfA, manifold.localPoint, b2WorldManifold.Initialize_s_planePoint);
                for (let i = 0; i < manifold.pointCount; ++i) {
                    const clipPoint = b2Transform.MultiplyVec2(xfB, manifold.points[i].localPoint, b2WorldManifold.Initialize_s_clipPoint);
                    const s = radiusA - b2Vec2.Dot(b2Vec2.Subtract(clipPoint, planePoint, b2Vec2.s_t0), this.normal);
                    const cA = b2Vec2.AddScaled(clipPoint, s, this.normal, b2WorldManifold.Initialize_s_cA);
                    const cB = b2Vec2.SubtractScaled(clipPoint, radiusB, this.normal, b2WorldManifold.Initialize_s_cB);
                    b2Vec2.Mid(cA, cB, this.points[i]);
                    this.separations[i] = b2Vec2.Dot(b2Vec2.Subtract(cB, cA, b2Vec2.s_t0), this.normal);
                }
                break;
            }
            case b2ManifoldType.e_faceB: {
                b2Rot.MultiplyVec2(xfB.q, manifold.localNormal, this.normal);
                const planePoint = b2Transform.MultiplyVec2(xfB, manifold.localPoint, b2WorldManifold.Initialize_s_planePoint);
                for (let i = 0; i < manifold.pointCount; ++i) {
                    const clipPoint = b2Transform.MultiplyVec2(xfA, manifold.points[i].localPoint, b2WorldManifold.Initialize_s_clipPoint);
                    const s = radiusB - b2Vec2.Dot(b2Vec2.Subtract(clipPoint, planePoint, b2Vec2.s_t0), this.normal);
                    const cB = b2Vec2.AddScaled(clipPoint, s, this.normal, b2WorldManifold.Initialize_s_cB);
                    const cA = b2Vec2.SubtractScaled(clipPoint, radiusA, this.normal, b2WorldManifold.Initialize_s_cA);
                    b2Vec2.Mid(cA, cB, this.points[i]);
                    this.separations[i] = b2Vec2.Dot(b2Vec2.Subtract(cA, cB, b2Vec2.s_t0), this.normal);
                }
                // Ensure normal points from A to B.
                this.normal.Negate();
                break;
            }
        }
    }
}
b2WorldManifold.Initialize_s_pointA = new b2Vec2();
b2WorldManifold.Initialize_s_pointB = new b2Vec2();
b2WorldManifold.Initialize_s_cA = new b2Vec2();
b2WorldManifold.Initialize_s_cB = new b2Vec2();
b2WorldManifold.Initialize_s_planePoint = new b2Vec2();
b2WorldManifold.Initialize_s_clipPoint = new b2Vec2();
/**
 * This is used for determining the state of contact points.
 */
var b2PointState;
(function (b2PointState) {
    /** Point does not exist */
    b2PointState[b2PointState["b2_nullState"] = 0] = "b2_nullState";
    /** Point was added in the update */
    b2PointState[b2PointState["b2_addState"] = 1] = "b2_addState";
    /** Point persisted across the update */
    b2PointState[b2PointState["b2_persistState"] = 2] = "b2_persistState";
    /** Point was removed in the update */
    b2PointState[b2PointState["b2_removeState"] = 3] = "b2_removeState";
})(b2PointState || (b2PointState = {}));
/**
 * Used for computing contact manifolds.
 */
class b2ClipVertex {
    constructor() {
        this.v = new b2Vec2();
        this.id = new b2ContactID();
    }
    Copy(other) {
        this.v.Copy(other.v);
        this.id.Copy(other.id);
        return this;
    }
}
new b2DistanceInput();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
/**
 * The body type.
 * static: zero mass, zero velocity, may be manually moved
 * kinematic: zero mass, non-zero velocity set by user, moved by solver
 * dynamic: positive mass, non-zero velocity determined by forces, moved by solver
 */
var b2BodyType;
(function (b2BodyType) {
    b2BodyType[b2BodyType["b2_staticBody"] = 0] = "b2_staticBody";
    b2BodyType[b2BodyType["b2_kinematicBody"] = 1] = "b2_kinematicBody";
    b2BodyType[b2BodyType["b2_dynamicBody"] = 2] = "b2_dynamicBody";
})(b2BodyType || (b2BodyType = {}));

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
({
    cA: new b2Vec2(),
    cB: new b2Vec2(),
    vs: b2MakeArray(4, b2Vec2),
    xf: new b2Transform(),
});

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
const b2TimeOfImpact_s_xfA = new b2Transform();
const b2TimeOfImpact_s_xfB = new b2Transform();
const b2TimeOfImpact_s_pointA = new b2Vec2();
const b2TimeOfImpact_s_pointB = new b2Vec2();
const b2TimeOfImpact_s_normal = new b2Vec2();
const b2TimeOfImpact_s_axisA = new b2Vec2();
const b2TimeOfImpact_s_axisB = new b2Vec2();
/**
 * Input parameters for b2TimeOfImpact
 */
class b2TOIInput {
    constructor() {
        this.proxyA = new b2DistanceProxy();
        this.proxyB = new b2DistanceProxy();
        this.sweepA = new b2Sweep();
        this.sweepB = new b2Sweep();
        this.tMax = 0; // defines sweep interval [0, tMax]
    }
}
var b2TOIOutputState;
(function (b2TOIOutputState) {
    b2TOIOutputState[b2TOIOutputState["e_unknown"] = 0] = "e_unknown";
    b2TOIOutputState[b2TOIOutputState["e_failed"] = 1] = "e_failed";
    b2TOIOutputState[b2TOIOutputState["e_overlapped"] = 2] = "e_overlapped";
    b2TOIOutputState[b2TOIOutputState["e_touching"] = 3] = "e_touching";
    b2TOIOutputState[b2TOIOutputState["e_separated"] = 4] = "e_separated";
})(b2TOIOutputState || (b2TOIOutputState = {}));
/**
 * Output parameters for b2TimeOfImpact.
 */
class b2TOIOutput {
    constructor() {
        this.state = b2TOIOutputState.e_unknown;
        this.t = 0;
    }
}
var b2SeparationFunctionType;
(function (b2SeparationFunctionType) {
    b2SeparationFunctionType[b2SeparationFunctionType["e_points"] = 0] = "e_points";
    b2SeparationFunctionType[b2SeparationFunctionType["e_faceA"] = 1] = "e_faceA";
    b2SeparationFunctionType[b2SeparationFunctionType["e_faceB"] = 2] = "e_faceB";
})(b2SeparationFunctionType || (b2SeparationFunctionType = {}));
class b2SeparationFunction {
    constructor() {
        this.m_sweepA = new b2Sweep();
        this.m_sweepB = new b2Sweep();
        this.m_type = b2SeparationFunctionType.e_points;
        this.m_localPoint = new b2Vec2();
        this.m_axis = new b2Vec2();
    }
    Initialize(cache, proxyA, sweepA, proxyB, sweepB, t1) {
        this.m_proxyA = proxyA;
        this.m_proxyB = proxyB;
        const { count } = cache;
        // DEBUG: b2Assert(0 < count && count < 3);
        this.m_sweepA.Copy(sweepA);
        this.m_sweepB.Copy(sweepB);
        const xfA = this.m_sweepA.GetTransform(b2TimeOfImpact_s_xfA, t1);
        const xfB = this.m_sweepB.GetTransform(b2TimeOfImpact_s_xfB, t1);
        if (count === 1) {
            this.m_type = b2SeparationFunctionType.e_points;
            const localPointA = this.m_proxyA.GetVertex(cache.indexA[0]);
            const localPointB = this.m_proxyB.GetVertex(cache.indexB[0]);
            const pointA = b2Transform.MultiplyVec2(xfA, localPointA, b2TimeOfImpact_s_pointA);
            const pointB = b2Transform.MultiplyVec2(xfB, localPointB, b2TimeOfImpact_s_pointB);
            b2Vec2.Subtract(pointB, pointA, this.m_axis);
            const s = this.m_axis.Normalize();
            return s;
        }
        if (cache.indexA[0] === cache.indexA[1]) {
            // Two points on B and one on A.
            this.m_type = b2SeparationFunctionType.e_faceB;
            const localPointB1 = this.m_proxyB.GetVertex(cache.indexB[0]);
            const localPointB2 = this.m_proxyB.GetVertex(cache.indexB[1]);
            b2Vec2.CrossVec2One(b2Vec2.Subtract(localPointB2, localPointB1, b2Vec2.s_t0), this.m_axis).Normalize();
            const normal = b2Rot.MultiplyVec2(xfB.q, this.m_axis, b2TimeOfImpact_s_normal);
            b2Vec2.Mid(localPointB1, localPointB2, this.m_localPoint);
            const pointB = b2Transform.MultiplyVec2(xfB, this.m_localPoint, b2TimeOfImpact_s_pointB);
            const localPointA = this.m_proxyA.GetVertex(cache.indexA[0]);
            const pointA = b2Transform.MultiplyVec2(xfA, localPointA, b2TimeOfImpact_s_pointA);
            let s = b2Vec2.Dot(b2Vec2.Subtract(pointA, pointB, b2Vec2.s_t0), normal);
            if (s < 0) {
                this.m_axis.Negate();
                s = -s;
            }
            return s;
        }
        // Two points on A and one or two points on B.
        this.m_type = b2SeparationFunctionType.e_faceA;
        const localPointA1 = this.m_proxyA.GetVertex(cache.indexA[0]);
        const localPointA2 = this.m_proxyA.GetVertex(cache.indexA[1]);
        b2Vec2.CrossVec2One(b2Vec2.Subtract(localPointA2, localPointA1, b2Vec2.s_t0), this.m_axis).Normalize();
        const normal = b2Rot.MultiplyVec2(xfA.q, this.m_axis, b2TimeOfImpact_s_normal);
        b2Vec2.Mid(localPointA1, localPointA2, this.m_localPoint);
        const pointA = b2Transform.MultiplyVec2(xfA, this.m_localPoint, b2TimeOfImpact_s_pointA);
        const localPointB = this.m_proxyB.GetVertex(cache.indexB[0]);
        const pointB = b2Transform.MultiplyVec2(xfB, localPointB, b2TimeOfImpact_s_pointB);
        let s = b2Vec2.Dot(b2Vec2.Subtract(pointB, pointA, b2Vec2.s_t0), normal);
        if (s < 0) {
            this.m_axis.Negate();
            s = -s;
        }
        return s;
    }
    FindMinSeparation(indexA, indexB, t) {
        const xfA = this.m_sweepA.GetTransform(b2TimeOfImpact_s_xfA, t);
        const xfB = this.m_sweepB.GetTransform(b2TimeOfImpact_s_xfB, t);
        switch (this.m_type) {
            case b2SeparationFunctionType.e_points: {
                const axisA = b2Rot.TransposeMultiplyVec2(xfA.q, this.m_axis, b2TimeOfImpact_s_axisA);
                const axisB = b2Rot.TransposeMultiplyVec2(xfB.q, b2Vec2.Negate(this.m_axis, b2Vec2.s_t0), b2TimeOfImpact_s_axisB);
                indexA[0] = this.m_proxyA.GetSupport(axisA);
                indexB[0] = this.m_proxyB.GetSupport(axisB);
                const localPointA = this.m_proxyA.GetVertex(indexA[0]);
                const localPointB = this.m_proxyB.GetVertex(indexB[0]);
                const pointA = b2Transform.MultiplyVec2(xfA, localPointA, b2TimeOfImpact_s_pointA);
                const pointB = b2Transform.MultiplyVec2(xfB, localPointB, b2TimeOfImpact_s_pointB);
                const separation = b2Vec2.Dot(b2Vec2.Subtract(pointB, pointA, b2Vec2.s_t0), this.m_axis);
                return separation;
            }
            case b2SeparationFunctionType.e_faceA: {
                const normal = b2Rot.MultiplyVec2(xfA.q, this.m_axis, b2TimeOfImpact_s_normal);
                const pointA = b2Transform.MultiplyVec2(xfA, this.m_localPoint, b2TimeOfImpact_s_pointA);
                const axisB = b2Rot.TransposeMultiplyVec2(xfB.q, b2Vec2.Negate(normal, b2Vec2.s_t0), b2TimeOfImpact_s_axisB);
                indexA[0] = -1;
                indexB[0] = this.m_proxyB.GetSupport(axisB);
                const localPointB = this.m_proxyB.GetVertex(indexB[0]);
                const pointB = b2Transform.MultiplyVec2(xfB, localPointB, b2TimeOfImpact_s_pointB);
                const separation = b2Vec2.Dot(b2Vec2.Subtract(pointB, pointA, b2Vec2.s_t0), normal);
                return separation;
            }
            case b2SeparationFunctionType.e_faceB: {
                const normal = b2Rot.MultiplyVec2(xfB.q, this.m_axis, b2TimeOfImpact_s_normal);
                const pointB = b2Transform.MultiplyVec2(xfB, this.m_localPoint, b2TimeOfImpact_s_pointB);
                const axisA = b2Rot.TransposeMultiplyVec2(xfA.q, b2Vec2.Negate(normal, b2Vec2.s_t0), b2TimeOfImpact_s_axisA);
                indexB[0] = -1;
                indexA[0] = this.m_proxyA.GetSupport(axisA);
                const localPointA = this.m_proxyA.GetVertex(indexA[0]);
                const pointA = b2Transform.MultiplyVec2(xfA, localPointA, b2TimeOfImpact_s_pointA);
                const separation = b2Vec2.Dot(b2Vec2.Subtract(pointA, pointB, b2Vec2.s_t0), normal);
                return separation;
            }
            default:
                // DEBUG: b2Assert(false);
                indexA[0] = -1;
                indexB[0] = -1;
                return 0;
        }
    }
    Evaluate(indexA, indexB, t) {
        const xfA = this.m_sweepA.GetTransform(b2TimeOfImpact_s_xfA, t);
        const xfB = this.m_sweepB.GetTransform(b2TimeOfImpact_s_xfB, t);
        switch (this.m_type) {
            case b2SeparationFunctionType.e_points: {
                const localPointA = this.m_proxyA.GetVertex(indexA);
                const localPointB = this.m_proxyB.GetVertex(indexB);
                const pointA = b2Transform.MultiplyVec2(xfA, localPointA, b2TimeOfImpact_s_pointA);
                const pointB = b2Transform.MultiplyVec2(xfB, localPointB, b2TimeOfImpact_s_pointB);
                const separation = b2Vec2.Dot(b2Vec2.Subtract(pointB, pointA, b2Vec2.s_t0), this.m_axis);
                return separation;
            }
            case b2SeparationFunctionType.e_faceA: {
                const normal = b2Rot.MultiplyVec2(xfA.q, this.m_axis, b2TimeOfImpact_s_normal);
                const pointA = b2Transform.MultiplyVec2(xfA, this.m_localPoint, b2TimeOfImpact_s_pointA);
                const localPointB = this.m_proxyB.GetVertex(indexB);
                const pointB = b2Transform.MultiplyVec2(xfB, localPointB, b2TimeOfImpact_s_pointB);
                const separation = b2Vec2.Dot(b2Vec2.Subtract(pointB, pointA, b2Vec2.s_t0), normal);
                return separation;
            }
            case b2SeparationFunctionType.e_faceB: {
                const normal = b2Rot.MultiplyVec2(xfB.q, this.m_axis, b2TimeOfImpact_s_normal);
                const pointB = b2Transform.MultiplyVec2(xfB, this.m_localPoint, b2TimeOfImpact_s_pointB);
                const localPointA = this.m_proxyA.GetVertex(indexA);
                const pointA = b2Transform.MultiplyVec2(xfA, localPointA, b2TimeOfImpact_s_pointA);
                const separation = b2Vec2.Dot(b2Vec2.Subtract(pointA, pointB, b2Vec2.s_t0), normal);
                return separation;
            }
            default:
                b2Assert(false);
                return 0;
        }
    }
}
new b2DistanceInput();
new b2SeparationFunction();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
[new b2ClipVertex(), new b2ClipVertex()];
[new b2ClipVertex(), new b2ClipVertex()];
[new b2ClipVertex(), new b2ClipVertex()];

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
new b2ContactID();
var b2EPAxisType;
(function (b2EPAxisType) {
    b2EPAxisType[b2EPAxisType["e_unknown"] = 0] = "e_unknown";
    b2EPAxisType[b2EPAxisType["e_edgeA"] = 1] = "e_edgeA";
    b2EPAxisType[b2EPAxisType["e_edgeB"] = 2] = "e_edgeB";
})(b2EPAxisType || (b2EPAxisType = {}));
/** This structure is used to keep track of the best separating axis. */
class b2EPAxis {
    constructor() {
        this.normal = new b2Vec2();
        this.type = b2EPAxisType.e_unknown;
        this.index = 0;
        this.separation = 0;
    }
}
/** This holds polygon B expressed in frame A. */
class b2TempPolygon {
    constructor() {
        this.vertices = b2MakeArray(b2_maxPolygonVertices, b2Vec2);
        this.normals = b2MakeArray(b2_maxPolygonVertices, b2Vec2);
        this.count = 0;
    }
}
new b2EPAxis();
new b2EPAxis();
new b2TempPolygon();
[new b2ClipVertex(), new b2ClipVertex()];
[new b2ClipVertex(), new b2ClipVertex()];
[new b2ClipVertex(), new b2ClipVertex()];

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
/**
 * A solid circle shape
 */
class b2CircleShape extends b2Shape {
    constructor(radius = 0) {
        super(b2ShapeType.e_circle, radius);
        /** Position */
        this.m_p = new b2Vec2();
    }
    Set(position, radius = this.m_radius) {
        this.m_p.Copy(position);
        this.m_radius = radius;
        return this;
    }
    /**
     * Implement b2Shape.
     */
    Clone() {
        return new b2CircleShape().Copy(this);
    }
    Copy(other) {
        super.Copy(other);
        // DEBUG: b2Assert(other instanceof b2CircleShape);
        this.m_p.Copy(other.m_p);
        return this;
    }
    /**
     * @see b2Shape::GetChildCount
     */
    GetChildCount() {
        return 1;
    }
    /**
     * Implement b2Shape.
     */
    TestPoint(transform, p) {
        const center = b2Transform.MultiplyVec2(transform, this.m_p, b2CircleShape.TestPoint_s_center);
        const d = b2Vec2.Subtract(p, center, b2CircleShape.TestPoint_s_d);
        return b2Vec2.Dot(d, d) <= Math.pow(this.m_radius, 2);
    }
    /**
     * Implement b2Shape.
     *
     * @note because the circle is solid, rays that start inside do not hit because the normal is
     * not defined. Collision Detection in Interactive 3D Environments by Gino van den Bergen
     * From Section 3.1.2
     * x = s + a * r
     * norm(x) = radius
     */
    RayCast(output, input, transform, _childIndex) {
        const position = b2Transform.MultiplyVec2(transform, this.m_p, b2CircleShape.RayCast_s_position);
        const s = b2Vec2.Subtract(input.p1, position, b2CircleShape.RayCast_s_s);
        const b = b2Vec2.Dot(s, s) - Math.pow(this.m_radius, 2);
        // Solve quadratic equation.
        const r = b2Vec2.Subtract(input.p2, input.p1, b2CircleShape.RayCast_s_r);
        const c = b2Vec2.Dot(s, r);
        const rr = b2Vec2.Dot(r, r);
        const sigma = c * c - rr * b;
        // Check for negative discriminant and short segment.
        if (sigma < 0 || rr < b2_epsilon) {
            return false;
        }
        // Find the point of intersection of the line with the circle.
        let a = -(c + Math.sqrt(sigma));
        // Is the intersection point on the segment?
        if (a >= 0 && a <= input.maxFraction * rr) {
            a /= rr;
            output.fraction = a;
            b2Vec2.AddScaled(s, a, r, output.normal).Normalize();
            return true;
        }
        return false;
    }
    /**
     * @see b2Shape::ComputeAABB
     */
    ComputeAABB(aabb, transform, _childIndex) {
        const p = b2Transform.MultiplyVec2(transform, this.m_p, b2CircleShape.ComputeAABB_s_p);
        aabb.lowerBound.Set(p.x - this.m_radius, p.y - this.m_radius);
        aabb.upperBound.Set(p.x + this.m_radius, p.y + this.m_radius);
    }
    /**
     * @see b2Shape::ComputeMass
     */
    ComputeMass(massData, density) {
        const radius_sq = Math.pow(this.m_radius, 2);
        massData.mass = density * Math.PI * radius_sq;
        massData.center.Copy(this.m_p);
        // inertia about the local origin
        massData.I = massData.mass * (0.5 * radius_sq + b2Vec2.Dot(this.m_p, this.m_p));
    }
    SetupDistanceProxy(proxy, _index) {
        proxy.m_vertices = proxy.m_buffer;
        proxy.m_vertices[0].Copy(this.m_p);
        proxy.m_count = 1;
        proxy.m_radius = this.m_radius;
    }
    Draw(draw, color) {
        const center = this.m_p;
        const radius = this.m_radius;
        const axis = b2Vec2.UNITX;
        draw.DrawSolidCircle(center, radius, axis, color);
    }
}
b2CircleShape.TestPoint_s_center = new b2Vec2();
b2CircleShape.TestPoint_s_d = new b2Vec2();
b2CircleShape.RayCast_s_position = new b2Vec2();
b2CircleShape.RayCast_s_s = new b2Vec2();
b2CircleShape.RayCast_s_r = new b2Vec2();
b2CircleShape.ComputeAABB_s_p = new b2Vec2();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
/**
 * A line segment (edge) shape. These can be connected in chains or loops
 * to other edge shapes. Edges created independently are two-sided and do
 * no provide smooth movement across junctions.
 */
class b2EdgeShape extends b2Shape {
    constructor() {
        super(b2ShapeType.e_edge, b2_polygonRadius);
        /** These are the edge vertices */
        this.m_vertex1 = new b2Vec2();
        this.m_vertex2 = new b2Vec2();
        /** Optional adjacent vertices. These are used for smooth collision. */
        this.m_vertex0 = new b2Vec2();
        this.m_vertex3 = new b2Vec2();
        /** Uses m_vertex0 and m_vertex3 to create smooth collision. */
        this.m_oneSided = false;
    }
    /**
     * Set this as a part of a sequence. Vertex v0 precedes the edge and vertex v3
     * follows. These extra vertices are used to provide smooth movement
     * across junctions. This also makes the collision one-sided. The edge
     * normal points to the right looking from v1 to v2.
     */
    SetOneSided(v0, v1, v2, v3) {
        this.m_vertex0.Copy(v0);
        this.m_vertex1.Copy(v1);
        this.m_vertex2.Copy(v2);
        this.m_vertex3.Copy(v3);
        this.m_oneSided = true;
        return this;
    }
    /**
     * Set this as an isolated edge. Collision is two-sided.
     */
    SetTwoSided(v1, v2) {
        this.m_vertex1.Copy(v1);
        this.m_vertex2.Copy(v2);
        this.m_oneSided = false;
        return this;
    }
    /**
     * Implement b2Shape.
     */
    Clone() {
        return new b2EdgeShape().Copy(this);
    }
    Copy(other) {
        super.Copy(other);
        // DEBUG: b2Assert(other instanceof b2EdgeShape);
        this.m_vertex1.Copy(other.m_vertex1);
        this.m_vertex2.Copy(other.m_vertex2);
        this.m_vertex0.Copy(other.m_vertex0);
        this.m_vertex3.Copy(other.m_vertex3);
        this.m_oneSided = other.m_oneSided;
        return this;
    }
    /**
     * @see b2Shape::GetChildCount
     */
    GetChildCount() {
        return 1;
    }
    /**
     * @see b2Shape::TestPoint
     */
    TestPoint(_xf, _p) {
        return false;
    }
    /**
     * Implement b2Shape.
     *
     * p = p1 + t * d
     * v = v1 + s * e
     * p1 + t * d = v1 + s * e
     * s * e - t * d = p1 - v1
     */
    RayCast(output, input, xf, _childIndex) {
        // Put the ray into the edge's frame of reference.
        const p1 = b2Transform.TransposeMultiplyVec2(xf, input.p1, b2EdgeShape.RayCast_s_p1);
        const p2 = b2Transform.TransposeMultiplyVec2(xf, input.p2, b2EdgeShape.RayCast_s_p2);
        const d = b2Vec2.Subtract(p2, p1, b2EdgeShape.RayCast_s_d);
        const v1 = this.m_vertex1;
        const v2 = this.m_vertex2;
        const e = b2Vec2.Subtract(v2, v1, b2EdgeShape.RayCast_s_e);
        // Normal points to the right, looking from v1 at v2
        const { normal } = output;
        normal.Set(e.y, -e.x).Normalize();
        // q = p1 + t * d
        // dot(normal, q - v1) = 0
        // dot(normal, p1 - v1) + t * dot(normal, d) = 0
        const numerator = b2Vec2.Dot(normal, b2Vec2.Subtract(v1, p1, b2Vec2.s_t0));
        if (this.m_oneSided && numerator > 0) {
            return false;
        }
        const denominator = b2Vec2.Dot(normal, d);
        if (denominator === 0) {
            return false;
        }
        const t = numerator / denominator;
        if (t < 0 || input.maxFraction < t) {
            return false;
        }
        const q = b2Vec2.AddScaled(p1, t, d, b2EdgeShape.RayCast_s_q);
        // q = v1 + s * r
        // s = dot(q - v1, r) / dot(r, r)
        const r = b2Vec2.Subtract(v2, v1, b2EdgeShape.RayCast_s_r);
        const rr = b2Vec2.Dot(r, r);
        if (rr === 0) {
            return false;
        }
        const s = b2Vec2.Dot(b2Vec2.Subtract(q, v1, b2Vec2.s_t0), r) / rr;
        if (s < 0 || s > 1) {
            return false;
        }
        output.fraction = t;
        b2Rot.MultiplyVec2(xf.q, output.normal, output.normal);
        if (numerator > 0) {
            output.normal.Negate();
        }
        return true;
    }
    /**
     * @see b2Shape::ComputeAABB
     */
    ComputeAABB(aabb, xf, _childIndex) {
        const v1 = b2Transform.MultiplyVec2(xf, this.m_vertex1, b2EdgeShape.ComputeAABB_s_v1);
        const v2 = b2Transform.MultiplyVec2(xf, this.m_vertex2, b2EdgeShape.ComputeAABB_s_v2);
        b2Vec2.Min(v1, v2, aabb.lowerBound);
        b2Vec2.Max(v1, v2, aabb.upperBound);
        const r = this.m_radius;
        aabb.lowerBound.SubtractXY(r, r);
        aabb.upperBound.AddXY(r, r);
    }
    /**
     * @see b2Shape::ComputeMass
     */
    ComputeMass(massData, _density) {
        massData.mass = 0;
        b2Vec2.Mid(this.m_vertex1, this.m_vertex2, massData.center);
        massData.I = 0;
    }
    SetupDistanceProxy(proxy, _index) {
        proxy.m_vertices = proxy.m_buffer;
        proxy.m_vertices[0].Copy(this.m_vertex1);
        proxy.m_vertices[1].Copy(this.m_vertex2);
        proxy.m_count = 2;
        proxy.m_radius = this.m_radius;
    }
    Draw(draw, color) {
        const v1 = this.m_vertex1;
        const v2 = this.m_vertex2;
        draw.DrawSegment(v1, v2, color);
        if (this.m_oneSided === false) {
            draw.DrawPoint(v1, 4, color);
            draw.DrawPoint(v2, 4, color);
        }
    }
}
b2EdgeShape.RayCast_s_p1 = new b2Vec2();
b2EdgeShape.RayCast_s_p2 = new b2Vec2();
b2EdgeShape.RayCast_s_d = new b2Vec2();
b2EdgeShape.RayCast_s_e = new b2Vec2();
b2EdgeShape.RayCast_s_q = new b2Vec2();
b2EdgeShape.RayCast_s_r = new b2Vec2();
b2EdgeShape.ComputeAABB_s_v1 = new b2Vec2();
b2EdgeShape.ComputeAABB_s_v2 = new b2Vec2();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert, b2_linearSlop } from "../common/b2_common";
/**
 * A chain shape is a free form sequence of line segments.
 * The chain has one-sided collision, with the surface normal pointing to the right of the edge.
 * This provides a counter-clockwise winding like the polygon shape.
 * Connectivity information is used to create smooth collisions.
 *
 * @warning the chain will not collide properly if there are self-intersections.
 */
class b2ChainShape extends b2Shape {
    constructor() {
        super(b2ShapeType.e_chain, b2_polygonRadius);
        this.m_vertices = [];
        this.m_prevVertex = new b2Vec2();
        this.m_nextVertex = new b2Vec2();
    }
    /**
     * Create a loop. This automatically adjusts connectivity.
     *
     * @param vertices An array of vertices, these are copied
     * @param count The vertex count
     */
    CreateLoop(vertices, count = vertices.length) {
        // DEBUG: b2Assert(count >= 3);
        if (count < 3) {
            return this;
        }
        // DEBUG: for (let i =  1; i < count; ++i) {
        // DEBUG:   const v1 = vertices[i - 1];
        // DEBUG:   const v2 = vertices[i];
        // DEBUG:   // If the code crashes here, it means your vertices are too close together.
        // DEBUG:   b2Assert(b2Vec2.DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop);
        // DEBUG: }
        this.m_vertices.length = count + 1;
        for (let i = 0; i < count; ++i) {
            const { x, y } = vertices[i];
            this.m_vertices[i] = new b2Vec2(x, y);
        }
        this.m_vertices[count] = this.m_vertices[0].Clone();
        this.m_prevVertex.Copy(this.m_vertices[this.m_vertices.length - 2]);
        this.m_nextVertex.Copy(this.m_vertices[1]);
        return this;
    }
    /**
     * Create a chain with ghost vertices to connect multiple chains together.
     *
     * @param vertices An array of vertices, these are copied
     * @param count The vertex count
     * @param prevVertex Previous vertex from chain that connects to the start
     * @param nextVertex Next vertex from chain that connects to the end
     */
    CreateChain(vertices, count, prevVertex, nextVertex) {
        // DEBUG: b2Assert(count >= 2);
        // DEBUG: for (let i =  1; i < count; ++i) {
        // DEBUG:   // If the code crashes here, it means your vertices are too close together.
        // DEBUG:   b2Assert(b2Vec2.DistanceSquared(vertices[i-1], vertices[i]) > b2_linearSlop * b2_linearSlop);
        // DEBUG: }
        this.m_vertices.length = count;
        for (let i = 0; i < count; ++i) {
            const { x, y } = vertices[i];
            this.m_vertices[i] = new b2Vec2(x, y);
        }
        this.m_prevVertex.Copy(prevVertex);
        this.m_nextVertex.Copy(nextVertex);
        return this;
    }
    /**
     * Implement b2Shape. Vertices are cloned using b2Alloc.
     */
    Clone() {
        return new b2ChainShape().Copy(this);
    }
    Copy(other) {
        super.Copy(other);
        // DEBUG: b2Assert(other instanceof b2ChainShape);
        return this.CreateChain(other.m_vertices, other.m_vertices.length, other.m_prevVertex, other.m_nextVertex);
    }
    /**
     * @see b2Shape::GetChildCount
     */
    GetChildCount() {
        // edge count = vertex count - 1
        return this.m_vertices.length - 1;
    }
    /**
     * Get a child edge.
     */
    GetChildEdge(edge, index) {
        // DEBUG: b2Assert(0 <= index && index < this.m_vertices.length - 1);
        edge.m_radius = this.m_radius;
        edge.m_vertex1.Copy(this.m_vertices[index]);
        edge.m_vertex2.Copy(this.m_vertices[index + 1]);
        edge.m_oneSided = true;
        if (index > 0) {
            edge.m_vertex0.Copy(this.m_vertices[index - 1]);
        }
        else {
            edge.m_vertex0.Copy(this.m_prevVertex);
        }
        if (index < this.m_vertices.length - 2) {
            edge.m_vertex3.Copy(this.m_vertices[index + 2]);
        }
        else {
            edge.m_vertex3.Copy(this.m_nextVertex);
        }
    }
    /**
     * This always return false.
     *
     * @see b2Shape::TestPoint
     */
    TestPoint(_xf, _p) {
        return false;
    }
    /**
     * Implement b2Shape.
     */
    RayCast(output, input, xf, childIndex) {
        // DEBUG: b2Assert(childIndex < this.m_vertices.length);
        const edgeShape = b2ChainShape.RayCast_s_edgeShape;
        const i1 = childIndex;
        let i2 = childIndex + 1;
        if (i2 === this.m_vertices.length) {
            i2 = 0;
        }
        edgeShape.m_vertex1.Copy(this.m_vertices[i1]);
        edgeShape.m_vertex2.Copy(this.m_vertices[i2]);
        return edgeShape.RayCast(output, input, xf, 0);
    }
    /**
     * @see b2Shape::ComputeAABB
     */
    ComputeAABB(aabb, xf, childIndex) {
        // DEBUG: b2Assert(childIndex < this.m_vertices.length);
        const i1 = childIndex;
        let i2 = childIndex + 1;
        if (i2 === this.m_vertices.length) {
            i2 = 0;
        }
        const v1 = b2Transform.MultiplyVec2(xf, this.m_vertices[i1], b2ChainShape.ComputeAABB_s_v1);
        const v2 = b2Transform.MultiplyVec2(xf, this.m_vertices[i2], b2ChainShape.ComputeAABB_s_v2);
        const lower = b2Vec2.Min(v1, v2, b2ChainShape.ComputeAABB_s_lower);
        const upper = b2Vec2.Max(v1, v2, b2ChainShape.ComputeAABB_s_upper);
        aabb.lowerBound.x = lower.x - this.m_radius;
        aabb.lowerBound.y = lower.y - this.m_radius;
        aabb.upperBound.x = upper.x + this.m_radius;
        aabb.upperBound.y = upper.y + this.m_radius;
    }
    /**
     * Chains have zero mass.
     *
     * @see b2Shape::ComputeMass
     */
    ComputeMass(massData, _density) {
        massData.mass = 0;
        massData.center.SetZero();
        massData.I = 0;
    }
    SetupDistanceProxy(proxy, index) {
        // DEBUG: b2Assert(0 <= index && index < this.m_vertices.length);
        proxy.m_vertices = proxy.m_buffer;
        proxy.m_vertices[0].Copy(this.m_vertices[index]);
        if (index + 1 < this.m_vertices.length) {
            proxy.m_vertices[1].Copy(this.m_vertices[index + 1]);
        }
        else {
            proxy.m_vertices[1].Copy(this.m_vertices[0]);
        }
        proxy.m_count = 2;
        proxy.m_radius = this.m_radius;
    }
    Draw(draw, color) {
        const vertices = this.m_vertices;
        let v1 = vertices[0];
        for (let i = 1; i < vertices.length; ++i) {
            const v2 = vertices[i];
            draw.DrawSegment(v1, v2, color);
            v1 = v2;
        }
    }
}
b2ChainShape.RayCast_s_edgeShape = new b2EdgeShape();
b2ChainShape.ComputeAABB_s_v1 = new b2Vec2();
b2ChainShape.ComputeAABB_s_v2 = new b2Vec2();
b2ChainShape.ComputeAABB_s_lower = new b2Vec2();
b2ChainShape.ComputeAABB_s_upper = new b2Vec2();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
var b2JointType;
(function (b2JointType) {
    b2JointType[b2JointType["e_unknownJoint"] = 0] = "e_unknownJoint";
    b2JointType[b2JointType["e_revoluteJoint"] = 1] = "e_revoluteJoint";
    b2JointType[b2JointType["e_prismaticJoint"] = 2] = "e_prismaticJoint";
    b2JointType[b2JointType["e_distanceJoint"] = 3] = "e_distanceJoint";
    b2JointType[b2JointType["e_pulleyJoint"] = 4] = "e_pulleyJoint";
    b2JointType[b2JointType["e_mouseJoint"] = 5] = "e_mouseJoint";
    b2JointType[b2JointType["e_gearJoint"] = 6] = "e_gearJoint";
    b2JointType[b2JointType["e_wheelJoint"] = 7] = "e_wheelJoint";
    b2JointType[b2JointType["e_weldJoint"] = 8] = "e_weldJoint";
    b2JointType[b2JointType["e_frictionJoint"] = 9] = "e_frictionJoint";
    b2JointType[b2JointType["e_motorJoint"] = 10] = "e_motorJoint";
    b2JointType[b2JointType["e_areaJoint"] = 11] = "e_areaJoint";
})(b2JointType || (b2JointType = {}));

// MIT License
new b2EdgeShape();

// MIT License
new b2EdgeShape();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/**
 * Contact impulses for reporting. Impulses are used instead of forces because
 * sub-step forces may approach infinity for rigid body collisions. These
 * match up one-to-one with the contact points in b2Manifold.
 */
class b2ContactImpulse {
    constructor() {
        this.normalImpulses = b2MakeNumberArray(b2_maxManifoldPoints);
        this.tangentImpulses = b2MakeNumberArray(b2_maxManifoldPoints);
        this.count = 0;
    }
}

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/**
 * This is an internal structure.
 */
class b2TimeStep {
    constructor() {
        this.dt = 0; // time step
        this.inv_dt = 0; // inverse time step (0 if dt == 0).
        this.dtRatio = 0; // dt * inv_dt0
        this.config = {
            velocityIterations: 0,
            positionIterations: 0,
        };
        this.warmStarting = false;
    }
    static Create() {
        return new b2TimeStep();
    }
    Copy(step) {
        this.dt = step.dt;
        this.inv_dt = step.inv_dt;
        this.dtRatio = step.dtRatio;
        this.config = Object.assign({}, step.config);
        this.warmStarting = step.warmStarting;
        return this;
    }
}

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
let g_blockSolve = true;
class b2VelocityConstraintPoint {
    constructor() {
        this.rA = new b2Vec2();
        this.rB = new b2Vec2();
        this.normalImpulse = 0;
        this.tangentImpulse = 0;
        this.normalMass = 0;
        this.tangentMass = 0;
        this.velocityBias = 0;
    }
}
/** @internal */
class b2ContactVelocityConstraint {
    constructor() {
        this.points = b2MakeArray(b2_maxManifoldPoints, b2VelocityConstraintPoint);
        this.normal = new b2Vec2();
        this.tangent = new b2Vec2();
        this.normalMass = new b2Mat22();
        this.K = new b2Mat22();
        this.indexA = 0;
        this.indexB = 0;
        this.invMassA = 0;
        this.invMassB = 0;
        this.invIA = 0;
        this.invIB = 0;
        this.friction = 0;
        this.restitution = 0;
        this.threshold = 0;
        this.tangentSpeed = 0;
        this.pointCount = 0;
        this.contactIndex = 0;
    }
}
class b2ContactPositionConstraint {
    constructor() {
        this.localPoints = b2MakeArray(b2_maxManifoldPoints, b2Vec2);
        this.localNormal = new b2Vec2();
        this.localPoint = new b2Vec2();
        this.indexA = 0;
        this.indexB = 0;
        this.invMassA = 0;
        this.invMassB = 0;
        this.localCenterA = new b2Vec2();
        this.localCenterB = new b2Vec2();
        this.invIA = 0;
        this.invIB = 0;
        this.type = b2ManifoldType.e_circles;
        this.radiusA = 0;
        this.radiusB = 0;
        this.pointCount = 0;
    }
}
class b2PositionSolverManifold {
    constructor() {
        this.normal = new b2Vec2();
        this.point = new b2Vec2();
        this.separation = 0;
    }
    Initialize(pc, xfA, xfB, index) {
        const pointA = b2PositionSolverManifold.Initialize_s_pointA;
        const pointB = b2PositionSolverManifold.Initialize_s_pointB;
        const planePoint = b2PositionSolverManifold.Initialize_s_planePoint;
        const clipPoint = b2PositionSolverManifold.Initialize_s_clipPoint;
        // DEBUG: b2Assert(pc.pointCount > 0);
        switch (pc.type) {
            case b2ManifoldType.e_circles:
                b2Transform.MultiplyVec2(xfA, pc.localPoint, pointA);
                b2Transform.MultiplyVec2(xfB, pc.localPoints[0], pointB);
                b2Vec2.Subtract(pointB, pointA, this.normal).Normalize();
                b2Vec2.Mid(pointA, pointB, this.point);
                this.separation =
                    b2Vec2.Dot(b2Vec2.Subtract(pointB, pointA, b2Vec2.s_t0), this.normal) - pc.radiusA - pc.radiusB;
                break;
            case b2ManifoldType.e_faceA:
                b2Rot.MultiplyVec2(xfA.q, pc.localNormal, this.normal);
                b2Transform.MultiplyVec2(xfA, pc.localPoint, planePoint);
                b2Transform.MultiplyVec2(xfB, pc.localPoints[index], clipPoint);
                this.separation =
                    b2Vec2.Dot(b2Vec2.Subtract(clipPoint, planePoint, b2Vec2.s_t0), this.normal) -
                        pc.radiusA -
                        pc.radiusB;
                this.point.Copy(clipPoint);
                break;
            case b2ManifoldType.e_faceB:
                b2Rot.MultiplyVec2(xfB.q, pc.localNormal, this.normal);
                b2Transform.MultiplyVec2(xfB, pc.localPoint, planePoint);
                b2Transform.MultiplyVec2(xfA, pc.localPoints[index], clipPoint);
                this.separation =
                    b2Vec2.Dot(b2Vec2.Subtract(clipPoint, planePoint, b2Vec2.s_t0), this.normal) -
                        pc.radiusA -
                        pc.radiusB;
                this.point.Copy(clipPoint);
                // Ensure normal points from A to B
                this.normal.Negate();
                break;
        }
    }
}
b2PositionSolverManifold.Initialize_s_pointA = new b2Vec2();
b2PositionSolverManifold.Initialize_s_pointB = new b2Vec2();
b2PositionSolverManifold.Initialize_s_planePoint = new b2Vec2();
b2PositionSolverManifold.Initialize_s_clipPoint = new b2Vec2();
/** @internal */
class b2ContactSolver {
    constructor() {
        this.m_step = b2TimeStep.Create();
        this.m_positionConstraints = b2MakeArray(1024, b2ContactPositionConstraint); // TODO: b2Settings
        this.m_velocityConstraints = b2MakeArray(1024, b2ContactVelocityConstraint); // TODO: b2Settings
        this.m_count = 0;
    }
    Initialize(def) {
        this.m_step.Copy(def.step);
        this.m_count = def.count;
        // TODO:
        if (this.m_positionConstraints.length < this.m_count) {
            const new_length = Math.max(this.m_positionConstraints.length * 2, this.m_count);
            while (this.m_positionConstraints.length < new_length) {
                this.m_positionConstraints[this.m_positionConstraints.length] = new b2ContactPositionConstraint();
            }
        }
        // TODO:
        if (this.m_velocityConstraints.length < this.m_count) {
            const new_length = Math.max(this.m_velocityConstraints.length * 2, this.m_count);
            while (this.m_velocityConstraints.length < new_length) {
                this.m_velocityConstraints[this.m_velocityConstraints.length] = new b2ContactVelocityConstraint();
            }
        }
        this.m_positions = def.positions;
        this.m_velocities = def.velocities;
        this.m_contacts = def.contacts;
        // Initialize position independent portions of the constraints.
        for (let i = 0; i < this.m_count; ++i) {
            const contact = this.m_contacts[i];
            const fixtureA = contact.m_fixtureA;
            const fixtureB = contact.m_fixtureB;
            const shapeA = fixtureA.GetShape();
            const shapeB = fixtureB.GetShape();
            const radiusA = shapeA.m_radius;
            const radiusB = shapeB.m_radius;
            const bodyA = fixtureA.GetBody();
            const bodyB = fixtureB.GetBody();
            const manifold = contact.GetManifold();
            const { pointCount } = manifold;
            // DEBUG: b2Assert(pointCount > 0);
            const vc = this.m_velocityConstraints[i];
            vc.friction = contact.m_friction;
            vc.restitution = contact.m_restitution;
            vc.threshold = contact.m_restitutionThreshold;
            vc.tangentSpeed = contact.m_tangentSpeed;
            vc.indexA = bodyA.m_islandIndex;
            vc.indexB = bodyB.m_islandIndex;
            vc.invMassA = bodyA.m_invMass;
            vc.invMassB = bodyB.m_invMass;
            vc.invIA = bodyA.m_invI;
            vc.invIB = bodyB.m_invI;
            vc.contactIndex = i;
            vc.pointCount = pointCount;
            vc.K.SetZero();
            vc.normalMass.SetZero();
            const pc = this.m_positionConstraints[i];
            pc.indexA = bodyA.m_islandIndex;
            pc.indexB = bodyB.m_islandIndex;
            pc.invMassA = bodyA.m_invMass;
            pc.invMassB = bodyB.m_invMass;
            pc.localCenterA.Copy(bodyA.m_sweep.localCenter);
            pc.localCenterB.Copy(bodyB.m_sweep.localCenter);
            pc.invIA = bodyA.m_invI;
            pc.invIB = bodyB.m_invI;
            pc.localNormal.Copy(manifold.localNormal);
            pc.localPoint.Copy(manifold.localPoint);
            pc.pointCount = pointCount;
            pc.radiusA = radiusA;
            pc.radiusB = radiusB;
            pc.type = manifold.type;
            for (let j = 0; j < pointCount; ++j) {
                const cp = manifold.points[j];
                const vcp = vc.points[j];
                if (this.m_step.warmStarting) {
                    vcp.normalImpulse = this.m_step.dtRatio * cp.normalImpulse;
                    vcp.tangentImpulse = this.m_step.dtRatio * cp.tangentImpulse;
                }
                else {
                    vcp.normalImpulse = 0;
                    vcp.tangentImpulse = 0;
                }
                vcp.rA.SetZero();
                vcp.rB.SetZero();
                vcp.normalMass = 0;
                vcp.tangentMass = 0;
                vcp.velocityBias = 0;
                pc.localPoints[j].Copy(cp.localPoint);
            }
        }
        return this;
    }
    InitializeVelocityConstraints() {
        const xfA = b2ContactSolver.InitializeVelocityConstraints_s_xfA;
        const xfB = b2ContactSolver.InitializeVelocityConstraints_s_xfB;
        const worldManifold = b2ContactSolver.InitializeVelocityConstraints_s_worldManifold;
        const k_maxConditionNumber = 1000;
        for (let i = 0; i < this.m_count; ++i) {
            const vc = this.m_velocityConstraints[i];
            const pc = this.m_positionConstraints[i];
            const { radiusA, radiusB, localCenterA, localCenterB } = pc;
            const manifold = this.m_contacts[vc.contactIndex].GetManifold();
            const { indexA, indexB, tangent, pointCount } = vc;
            const mA = vc.invMassA;
            const mB = vc.invMassB;
            const iA = vc.invIA;
            const iB = vc.invIB;
            const cA = this.m_positions[indexA].c;
            const aA = this.m_positions[indexA].a;
            const vA = this.m_velocities[indexA].v;
            const wA = this.m_velocities[indexA].w;
            const cB = this.m_positions[indexB].c;
            const aB = this.m_positions[indexB].a;
            const vB = this.m_velocities[indexB].v;
            const wB = this.m_velocities[indexB].w;
            // DEBUG: b2Assert(manifold.pointCount > 0);
            xfA.q.Set(aA);
            xfB.q.Set(aB);
            b2Vec2.Subtract(cA, b2Rot.MultiplyVec2(xfA.q, localCenterA, b2Vec2.s_t0), xfA.p);
            b2Vec2.Subtract(cB, b2Rot.MultiplyVec2(xfB.q, localCenterB, b2Vec2.s_t0), xfB.p);
            worldManifold.Initialize(manifold, xfA, radiusA, xfB, radiusB);
            vc.normal.Copy(worldManifold.normal);
            b2Vec2.CrossVec2One(vc.normal, tangent); // compute from normal
            for (let j = 0; j < pointCount; ++j) {
                const vcp = vc.points[j];
                b2Vec2.Subtract(worldManifold.points[j], cA, vcp.rA);
                b2Vec2.Subtract(worldManifold.points[j], cB, vcp.rB);
                const rnA = b2Vec2.Cross(vcp.rA, vc.normal);
                const rnB = b2Vec2.Cross(vcp.rB, vc.normal);
                const kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
                vcp.normalMass = kNormal > 0 ? 1 / kNormal : 0;
                const rtA = b2Vec2.Cross(vcp.rA, tangent);
                const rtB = b2Vec2.Cross(vcp.rB, tangent);
                const kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;
                vcp.tangentMass = kTangent > 0 ? 1 / kTangent : 0;
                // Setup a velocity bias for restitution.
                vcp.velocityBias = 0;
                const vRel = b2Vec2.Dot(vc.normal, b2Vec2.Subtract(b2Vec2.AddCrossScalarVec2(vB, wB, vcp.rB, b2Vec2.s_t0), b2Vec2.AddCrossScalarVec2(vA, wA, vcp.rA, b2Vec2.s_t1), b2Vec2.s_t0));
                if (vRel < -vc.threshold) {
                    vcp.velocityBias = -vc.restitution * vRel;
                }
            }
            // If we have two points, then prepare the block solver.
            if (vc.pointCount === 2 && g_blockSolve) {
                const vcp1 = vc.points[0];
                const vcp2 = vc.points[1];
                const rn1A = b2Vec2.Cross(vcp1.rA, vc.normal);
                const rn1B = b2Vec2.Cross(vcp1.rB, vc.normal);
                const rn2A = b2Vec2.Cross(vcp2.rA, vc.normal);
                const rn2B = b2Vec2.Cross(vcp2.rB, vc.normal);
                const k11 = mA + mB + iA * rn1A * rn1A + iB * rn1B * rn1B;
                const k22 = mA + mB + iA * rn2A * rn2A + iB * rn2B * rn2B;
                const k12 = mA + mB + iA * rn1A * rn2A + iB * rn1B * rn2B;
                // Ensure a reasonable condition number.
                if (k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) {
                    // K is safe to invert.
                    vc.K.ex.Set(k11, k12);
                    vc.K.ey.Set(k12, k22);
                    vc.K.GetInverse(vc.normalMass);
                }
                else {
                    // The constraints are redundant, just use one.
                    // TODO_ERIN use deepest?
                    vc.pointCount = 1;
                }
            }
        }
    }
    WarmStart() {
        const P = b2ContactSolver.WarmStart_s_P;
        // Warm start.
        for (let i = 0; i < this.m_count; ++i) {
            const vc = this.m_velocityConstraints[i];
            const { indexA, indexB, pointCount, normal, tangent } = vc;
            const mA = vc.invMassA;
            const iA = vc.invIA;
            const mB = vc.invMassB;
            const iB = vc.invIB;
            const vA = this.m_velocities[indexA].v;
            let wA = this.m_velocities[indexA].w;
            const vB = this.m_velocities[indexB].v;
            let wB = this.m_velocities[indexB].w;
            for (let j = 0; j < pointCount; ++j) {
                const vcp = vc.points[j];
                b2Vec2.Add(b2Vec2.Scale(vcp.normalImpulse, normal, b2Vec2.s_t0), b2Vec2.Scale(vcp.tangentImpulse, tangent, b2Vec2.s_t1), P);
                wA -= iA * b2Vec2.Cross(vcp.rA, P);
                vA.SubtractScaled(mA, P);
                wB += iB * b2Vec2.Cross(vcp.rB, P);
                vB.AddScaled(mB, P);
            }
            this.m_velocities[indexA].w = wA;
            this.m_velocities[indexB].w = wB;
        }
    }
    SolveVelocityConstraints() {
        const dv = b2ContactSolver.SolveVelocityConstraints_s_dv;
        const dv1 = b2ContactSolver.SolveVelocityConstraints_s_dv1;
        const dv2 = b2ContactSolver.SolveVelocityConstraints_s_dv2;
        const P = b2ContactSolver.SolveVelocityConstraints_s_P;
        const a = b2ContactSolver.SolveVelocityConstraints_s_a;
        const b = b2ContactSolver.SolveVelocityConstraints_s_b;
        const x = b2ContactSolver.SolveVelocityConstraints_s_x;
        const d = b2ContactSolver.SolveVelocityConstraints_s_d;
        const P1 = b2ContactSolver.SolveVelocityConstraints_s_P1;
        const P2 = b2ContactSolver.SolveVelocityConstraints_s_P2;
        const P1P2 = b2ContactSolver.SolveVelocityConstraints_s_P1P2;
        for (let i = 0; i < this.m_count; ++i) {
            const vc = this.m_velocityConstraints[i];
            const { indexA, indexB, pointCount, normal, tangent, friction } = vc;
            const mA = vc.invMassA;
            const iA = vc.invIA;
            const mB = vc.invMassB;
            const iB = vc.invIB;
            const vA = this.m_velocities[indexA].v;
            let wA = this.m_velocities[indexA].w;
            const vB = this.m_velocities[indexB].v;
            let wB = this.m_velocities[indexB].w;
            // DEBUG: b2Assert(pointCount === 1 || pointCount === 2);
            // Solve tangent constraints first because non-penetration is more important
            // than friction.
            for (let j = 0; j < pointCount; ++j) {
                const vcp = vc.points[j];
                // Relative velocity at contact
                b2Vec2.Subtract(b2Vec2.AddCrossScalarVec2(vB, wB, vcp.rB, b2Vec2.s_t0), b2Vec2.AddCrossScalarVec2(vA, wA, vcp.rA, b2Vec2.s_t1), dv);
                // Compute tangent force
                const vt = b2Vec2.Dot(dv, tangent) - vc.tangentSpeed;
                let lambda = vcp.tangentMass * -vt;
                // b2Clamp the accumulated force
                const maxFriction = friction * vcp.normalImpulse;
                const newImpulse = b2Clamp(vcp.tangentImpulse + lambda, -maxFriction, maxFriction);
                lambda = newImpulse - vcp.tangentImpulse;
                vcp.tangentImpulse = newImpulse;
                // Apply contact impulse
                b2Vec2.Scale(lambda, tangent, P);
                vA.SubtractScaled(mA, P);
                wA -= iA * b2Vec2.Cross(vcp.rA, P);
                vB.AddScaled(mB, P);
                wB += iB * b2Vec2.Cross(vcp.rB, P);
            }
            // Solve normal constraints
            if (vc.pointCount === 1 || g_blockSolve === false) {
                for (let j = 0; j < pointCount; ++j) {
                    const vcp = vc.points[j];
                    // Relative velocity at contact
                    b2Vec2.Subtract(b2Vec2.AddCrossScalarVec2(vB, wB, vcp.rB, b2Vec2.s_t0), b2Vec2.AddCrossScalarVec2(vA, wA, vcp.rA, b2Vec2.s_t1), dv);
                    // Compute normal impulse
                    const vn = b2Vec2.Dot(dv, normal);
                    let lambda = -vcp.normalMass * (vn - vcp.velocityBias);
                    // b2Clamp the accumulated impulse
                    const newImpulse = Math.max(vcp.normalImpulse + lambda, 0);
                    lambda = newImpulse - vcp.normalImpulse;
                    vcp.normalImpulse = newImpulse;
                    // Apply contact impulse
                    b2Vec2.Scale(lambda, normal, P);
                    vA.SubtractScaled(mA, P);
                    wA -= iA * b2Vec2.Cross(vcp.rA, P);
                    vB.AddScaled(mB, P);
                    wB += iB * b2Vec2.Cross(vcp.rB, P);
                }
            }
            else {
                // Block solver developed in collaboration with Dirk Gregorius (back in 01/07 on Box2D_Lite).
                // Build the mini LCP for this contact patch
                //
                // vn = A * x + b, vn >= 0, x >= 0 and vn_i * x_i = 0 with i = 1..2
                //
                // A = J * W * JT and J = ( -n, -r1 x n, n, r2 x n )
                // b = vn0 - velocityBias
                //
                // The system is solved using the "Total enumeration method" (s. Murty). The complementary constraint vn_i * x_i
                // implies that we must have in any solution either vn_i = 0 or x_i = 0. So for the 2D contact problem the cases
                // vn1 = 0 and vn2 = 0, x1 = 0 and x2 = 0, x1 = 0 and vn2 = 0, x2 = 0 and vn1 = 0 need to be tested. The first valid
                // solution that satisfies the problem is chosen.
                //
                // In order to account of the accumulated impulse 'a' (because of the iterative nature of the solver which only requires
                // that the accumulated impulse is clamped and not the incremental impulse) we change the impulse variable (x_i).
                //
                // Substitute:
                //
                // x = a + d
                //
                // a := old total impulse
                // x := new total impulse
                // d := incremental impulse
                //
                // For the current iteration we extend the formula for the incremental impulse
                // to compute the new total impulse:
                //
                // vn = A * d + b
                //    = A * (x - a) + b
                //    = A * x + b - A * a
                //    = A * x + b'
                // b' = b - A * a;
                const cp1 = vc.points[0];
                const cp2 = vc.points[1];
                a.Set(cp1.normalImpulse, cp2.normalImpulse);
                // DEBUG: b2Assert(a.x >= 0 && a.y >= 0);
                // Relative velocity at contact
                b2Vec2.Subtract(b2Vec2.AddCrossScalarVec2(vB, wB, cp1.rB, b2Vec2.s_t0), b2Vec2.AddCrossScalarVec2(vA, wA, cp1.rA, b2Vec2.s_t1), dv1);
                b2Vec2.Subtract(b2Vec2.AddCrossScalarVec2(vB, wB, cp2.rB, b2Vec2.s_t0), b2Vec2.AddCrossScalarVec2(vA, wA, cp2.rA, b2Vec2.s_t1), dv2);
                // Compute normal velocity
                let vn1 = b2Vec2.Dot(dv1, normal);
                let vn2 = b2Vec2.Dot(dv2, normal);
                b.x = vn1 - cp1.velocityBias;
                b.y = vn2 - cp2.velocityBias;
                // Compute b'
                b.Subtract(b2Mat22.MultiplyVec2(vc.K, a, b2Vec2.s_t0));
                // eslint-disable-next-line no-unreachable-loop
                for (;;) {
                    //
                    // Case 1: vn = 0
                    //
                    // 0 = A * x + b'
                    //
                    // Solve for x:
                    //
                    // x = - inv(A) * b'
                    //
                    // b2Vec2 x = - b2Mul(vc->normalMass, b);
                    b2Mat22.MultiplyVec2(vc.normalMass, b, x).Negate();
                    if (x.x >= 0 && x.y >= 0) {
                        // Get the incremental impulse
                        // b2Vec2 d = x - a;
                        b2Vec2.Subtract(x, a, d);
                        // Apply incremental impulse
                        b2Vec2.Scale(d.x, normal, P1);
                        b2Vec2.Scale(d.y, normal, P2);
                        b2Vec2.Add(P1, P2, P1P2);
                        vA.SubtractScaled(mA, P1P2);
                        wA -= iA * (b2Vec2.Cross(cp1.rA, P1) + b2Vec2.Cross(cp2.rA, P2));
                        vB.AddScaled(mB, P1P2);
                        wB += iB * (b2Vec2.Cross(cp1.rB, P1) + b2Vec2.Cross(cp2.rB, P2));
                        // Accumulate
                        cp1.normalImpulse = x.x;
                        cp2.normalImpulse = x.y;
                        break;
                    }
                    //
                    // Case 2: vn1 = 0 and x2 = 0
                    //
                    //   0 = a11 * x1 + a12 * 0 + b1'
                    // vn2 = a21 * x1 + a22 * 0 + b2'
                    //
                    x.x = -cp1.normalMass * b.x;
                    x.y = 0;
                    vn1 = 0;
                    vn2 = vc.K.ex.y * x.x + b.y;
                    if (x.x >= 0 && vn2 >= 0) {
                        // Get the incremental impulse
                        // b2Vec2 d = x - a;
                        b2Vec2.Subtract(x, a, d);
                        // Apply incremental impulse
                        b2Vec2.Scale(d.x, normal, P1);
                        b2Vec2.Scale(d.y, normal, P2);
                        b2Vec2.Add(P1, P2, P1P2);
                        vA.SubtractScaled(mA, P1P2);
                        wA -= iA * (b2Vec2.Cross(cp1.rA, P1) + b2Vec2.Cross(cp2.rA, P2));
                        vB.AddScaled(mB, P1P2);
                        wB += iB * (b2Vec2.Cross(cp1.rB, P1) + b2Vec2.Cross(cp2.rB, P2));
                        // Accumulate
                        cp1.normalImpulse = x.x;
                        cp2.normalImpulse = x.y;
                        break;
                    }
                    //
                    // Case 3: vn2 = 0 and x1 = 0
                    //
                    // vn1 = a11 * 0 + a12 * x2 + b1'
                    //   0 = a21 * 0 + a22 * x2 + b2'
                    //
                    x.x = 0;
                    x.y = -cp2.normalMass * b.y;
                    vn1 = vc.K.ey.x * x.y + b.x;
                    vn2 = 0;
                    if (x.y >= 0 && vn1 >= 0) {
                        // Resubstitute for the incremental impulse
                        b2Vec2.Subtract(x, a, d);
                        // Apply incremental impulse
                        b2Vec2.Scale(d.x, normal, P1);
                        b2Vec2.Scale(d.y, normal, P2);
                        b2Vec2.Add(P1, P2, P1P2);
                        vA.SubtractScaled(mA, P1P2);
                        wA -= iA * (b2Vec2.Cross(cp1.rA, P1) + b2Vec2.Cross(cp2.rA, P2));
                        vB.AddScaled(mB, P1P2);
                        wB += iB * (b2Vec2.Cross(cp1.rB, P1) + b2Vec2.Cross(cp2.rB, P2));
                        // Accumulate
                        cp1.normalImpulse = x.x;
                        cp2.normalImpulse = x.y;
                        break;
                    }
                    //
                    // Case 4: x1 = 0 and x2 = 0
                    //
                    // vn1 = b1
                    // vn2 = b2;
                    x.x = 0;
                    x.y = 0;
                    vn1 = b.x;
                    vn2 = b.y;
                    if (vn1 >= 0 && vn2 >= 0) {
                        // Resubstitute for the incremental impulse
                        b2Vec2.Subtract(x, a, d);
                        // Apply incremental impulse
                        b2Vec2.Scale(d.x, normal, P1);
                        b2Vec2.Scale(d.y, normal, P2);
                        b2Vec2.Add(P1, P2, P1P2);
                        vA.SubtractScaled(mA, P1P2);
                        wA -= iA * (b2Vec2.Cross(cp1.rA, P1) + b2Vec2.Cross(cp2.rA, P2));
                        vB.AddScaled(mB, P1P2);
                        wB += iB * (b2Vec2.Cross(cp1.rB, P1) + b2Vec2.Cross(cp2.rB, P2));
                        // Accumulate
                        cp1.normalImpulse = x.x;
                        cp2.normalImpulse = x.y;
                        break;
                    }
                    // No solution, give up. This is hit sometimes, but it doesn't seem to matter.
                    break;
                }
            }
            this.m_velocities[indexA].w = wA;
            this.m_velocities[indexB].w = wB;
        }
    }
    StoreImpulses() {
        for (let i = 0; i < this.m_count; ++i) {
            const vc = this.m_velocityConstraints[i];
            const manifold = this.m_contacts[vc.contactIndex].GetManifold();
            for (let j = 0; j < vc.pointCount; ++j) {
                manifold.points[j].normalImpulse = vc.points[j].normalImpulse;
                manifold.points[j].tangentImpulse = vc.points[j].tangentImpulse;
            }
        }
    }
    SolvePositionConstraints() {
        const xfA = b2ContactSolver.SolvePositionConstraints_s_xfA;
        const xfB = b2ContactSolver.SolvePositionConstraints_s_xfB;
        const psm = b2ContactSolver.SolvePositionConstraints_s_psm;
        const rA = b2ContactSolver.SolvePositionConstraints_s_rA;
        const rB = b2ContactSolver.SolvePositionConstraints_s_rB;
        const P = b2ContactSolver.SolvePositionConstraints_s_P;
        let minSeparation = 0;
        for (let i = 0; i < this.m_count; ++i) {
            const pc = this.m_positionConstraints[i];
            const { indexA, indexB, localCenterA, localCenterB, pointCount } = pc;
            const mA = pc.invMassA;
            const iA = pc.invIA;
            const mB = pc.invMassB;
            const iB = pc.invIB;
            const cA = this.m_positions[indexA].c;
            let aA = this.m_positions[indexA].a;
            const cB = this.m_positions[indexB].c;
            let aB = this.m_positions[indexB].a;
            // Solve normal constraints
            for (let j = 0; j < pointCount; ++j) {
                xfA.q.Set(aA);
                xfB.q.Set(aB);
                b2Vec2.Subtract(cA, b2Rot.MultiplyVec2(xfA.q, localCenterA, b2Vec2.s_t0), xfA.p);
                b2Vec2.Subtract(cB, b2Rot.MultiplyVec2(xfB.q, localCenterB, b2Vec2.s_t0), xfB.p);
                psm.Initialize(pc, xfA, xfB, j);
                const { normal, point, separation } = psm;
                b2Vec2.Subtract(point, cA, rA);
                b2Vec2.Subtract(point, cB, rB);
                // Track max constraint error.
                minSeparation = Math.min(minSeparation, separation);
                // Prevent large corrections and allow slop.
                const C = b2Clamp(b2_baumgarte * (separation + b2_linearSlop), -b2_maxLinearCorrection, 0);
                // Compute the effective mass.
                const rnA = b2Vec2.Cross(rA, normal);
                const rnB = b2Vec2.Cross(rB, normal);
                const K = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
                // Compute normal impulse
                const impulse = K > 0 ? -C / K : 0;
                b2Vec2.Scale(impulse, normal, P);
                cA.SubtractScaled(mA, P);
                aA -= iA * b2Vec2.Cross(rA, P);
                cB.AddScaled(mB, P);
                aB += iB * b2Vec2.Cross(rB, P);
            }
            this.m_positions[indexA].c.Copy(cA);
            this.m_positions[indexA].a = aA;
            this.m_positions[indexB].c.Copy(cB);
            this.m_positions[indexB].a = aB;
        }
        // We can't expect minSpeparation >= -b2_linearSlop because we don't
        // push the separation above -b2_linearSlop.
        return minSeparation >= -3 * b2_linearSlop;
    }
    SolveTOIPositionConstraints(toiIndexA, toiIndexB) {
        const xfA = b2ContactSolver.SolveTOIPositionConstraints_s_xfA;
        const xfB = b2ContactSolver.SolveTOIPositionConstraints_s_xfB;
        const psm = b2ContactSolver.SolveTOIPositionConstraints_s_psm;
        const rA = b2ContactSolver.SolveTOIPositionConstraints_s_rA;
        const rB = b2ContactSolver.SolveTOIPositionConstraints_s_rB;
        const P = b2ContactSolver.SolveTOIPositionConstraints_s_P;
        let minSeparation = 0;
        for (let i = 0; i < this.m_count; ++i) {
            const pc = this.m_positionConstraints[i];
            const { indexA, indexB, localCenterA, localCenterB, pointCount } = pc;
            let mA = 0;
            let iA = 0;
            if (indexA === toiIndexA || indexA === toiIndexB) {
                mA = pc.invMassA;
                iA = pc.invIA;
            }
            let mB = 0;
            let iB = 0;
            if (indexB === toiIndexA || indexB === toiIndexB) {
                mB = pc.invMassB;
                iB = pc.invIB;
            }
            const cA = this.m_positions[indexA].c;
            let aA = this.m_positions[indexA].a;
            const cB = this.m_positions[indexB].c;
            let aB = this.m_positions[indexB].a;
            // Solve normal constraints
            for (let j = 0; j < pointCount; ++j) {
                xfA.q.Set(aA);
                xfB.q.Set(aB);
                b2Vec2.Subtract(cA, b2Rot.MultiplyVec2(xfA.q, localCenterA, b2Vec2.s_t0), xfA.p);
                b2Vec2.Subtract(cB, b2Rot.MultiplyVec2(xfB.q, localCenterB, b2Vec2.s_t0), xfB.p);
                psm.Initialize(pc, xfA, xfB, j);
                const { normal, point, separation } = psm;
                b2Vec2.Subtract(point, cA, rA);
                b2Vec2.Subtract(point, cB, rB);
                // Track max constraint error.
                minSeparation = Math.min(minSeparation, separation);
                // Prevent large corrections and allow slop.
                const C = b2Clamp(b2_toiBaumgarte * (separation + b2_linearSlop), -b2_maxLinearCorrection, 0);
                // Compute the effective mass.
                const rnA = b2Vec2.Cross(rA, normal);
                const rnB = b2Vec2.Cross(rB, normal);
                const K = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
                // Compute normal impulse
                const impulse = K > 0 ? -C / K : 0;
                b2Vec2.Scale(impulse, normal, P);
                cA.SubtractScaled(mA, P);
                aA -= iA * b2Vec2.Cross(rA, P);
                cB.AddScaled(mB, P);
                aB += iB * b2Vec2.Cross(rB, P);
            }
            this.m_positions[indexA].a = aA;
            this.m_positions[indexB].a = aB;
        }
        // We can't expect minSpeparation >= -b2_linearSlop because we don't
        // push the separation above -b2_linearSlop.
        return minSeparation >= -1.5 * b2_linearSlop;
    }
}
b2ContactSolver.InitializeVelocityConstraints_s_xfA = new b2Transform();
b2ContactSolver.InitializeVelocityConstraints_s_xfB = new b2Transform();
b2ContactSolver.InitializeVelocityConstraints_s_worldManifold = new b2WorldManifold();
b2ContactSolver.WarmStart_s_P = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_dv = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_dv1 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_dv2 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_a = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_b = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_x = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_d = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P1 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P2 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P1P2 = new b2Vec2();
b2ContactSolver.SolvePositionConstraints_s_xfA = new b2Transform();
b2ContactSolver.SolvePositionConstraints_s_xfB = new b2Transform();
b2ContactSolver.SolvePositionConstraints_s_psm = new b2PositionSolverManifold();
b2ContactSolver.SolvePositionConstraints_s_rA = new b2Vec2();
b2ContactSolver.SolvePositionConstraints_s_rB = new b2Vec2();
b2ContactSolver.SolvePositionConstraints_s_P = new b2Vec2();
b2ContactSolver.SolveTOIPositionConstraints_s_xfA = new b2Transform();
b2ContactSolver.SolveTOIPositionConstraints_s_xfB = new b2Transform();
b2ContactSolver.SolveTOIPositionConstraints_s_psm = new b2PositionSolverManifold();
b2ContactSolver.SolveTOIPositionConstraints_s_rA = new b2Vec2();
b2ContactSolver.SolveTOIPositionConstraints_s_rB = new b2Vec2();
b2ContactSolver.SolveTOIPositionConstraints_s_P = new b2Vec2();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
new b2ContactSolver();
new b2ContactImpulse();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// DEBUG: import { b2Assert } from "../common/b2_common";
new b2TOIInput();
new b2TOIOutput();

// MIT License
// Copyright (c) 2019 Erin Catto
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
var b2StretchingModel;
(function (b2StretchingModel) {
    b2StretchingModel[b2StretchingModel["b2_pbdStretchingModel"] = 0] = "b2_pbdStretchingModel";
    b2StretchingModel[b2StretchingModel["b2_xpbdStretchingModel"] = 1] = "b2_xpbdStretchingModel";
})(b2StretchingModel || (b2StretchingModel = {}));
var b2BendingModel;
(function (b2BendingModel) {
    b2BendingModel[b2BendingModel["b2_springAngleBendingModel"] = 0] = "b2_springAngleBendingModel";
    b2BendingModel[b2BendingModel["b2_pbdAngleBendingModel"] = 1] = "b2_pbdAngleBendingModel";
    b2BendingModel[b2BendingModel["b2_xpbdAngleBendingModel"] = 2] = "b2_xpbdAngleBendingModel";
    b2BendingModel[b2BendingModel["b2_pbdDistanceBendingModel"] = 3] = "b2_pbdDistanceBendingModel";
    b2BendingModel[b2BendingModel["b2_pbdHeightBendingModel"] = 4] = "b2_pbdHeightBendingModel";
    b2BendingModel[b2BendingModel["b2_pbdTriangleBendingModel"] = 5] = "b2_pbdTriangleBendingModel";
})(b2BendingModel || (b2BendingModel = {}));

//开发一个简洁的UI系统
var QUI_DragDriection;
(function (QUI_DragDriection) {
    QUI_DragDriection[QUI_DragDriection["None"] = 0] = "None";
    QUI_DragDriection[QUI_DragDriection["LeftToRight"] = 1] = "LeftToRight";
    QUI_DragDriection[QUI_DragDriection["UpToDown"] = 2] = "UpToDown";
    QUI_DragDriection[QUI_DragDriection["Both"] = 3] = "Both";
})(QUI_DragDriection || (QUI_DragDriection = {}));
var QUI_Direction;
(function (QUI_Direction) {
    QUI_Direction[QUI_Direction["LeftToRight"] = 0] = "LeftToRight";
    QUI_Direction[QUI_Direction["UpToDown"] = 1] = "UpToDown";
    QUI_Direction[QUI_Direction["RightToLeft"] = 2] = "RightToLeft";
    QUI_Direction[QUI_Direction["DownToUp"] = 3] = "DownToUp";
})(QUI_Direction || (QUI_Direction = {}));
var QUI_Direction2;
(function (QUI_Direction2) {
    QUI_Direction2[QUI_Direction2["Horizontal"] = 0] = "Horizontal";
    QUI_Direction2[QUI_Direction2["Vertical"] = 1] = "Vertical";
})(QUI_Direction2 || (QUI_Direction2 = {}));
var QUI_HAlign;
(function (QUI_HAlign) {
    QUI_HAlign[QUI_HAlign["Left"] = 0] = "Left";
    QUI_HAlign[QUI_HAlign["Middle"] = 1] = "Middle";
    QUI_HAlign[QUI_HAlign["Right"] = 2] = "Right";
})(QUI_HAlign || (QUI_HAlign = {}));
var QUI_VAlign;
(function (QUI_VAlign) {
    QUI_VAlign[QUI_VAlign["Top"] = 0] = "Top";
    QUI_VAlign[QUI_VAlign["Middle"] = 1] = "Middle";
    QUI_VAlign[QUI_VAlign["Bottom"] = 2] = "Bottom";
})(QUI_VAlign || (QUI_VAlign = {}));
var QUI_ElementType;
(function (QUI_ElementType) {
    QUI_ElementType[QUI_ElementType["Element_Canvas"] = 0] = "Element_Canvas";
    //基本控件
    QUI_ElementType[QUI_ElementType["Element_Container"] = 1] = "Element_Container";
    QUI_ElementType[QUI_ElementType["Element_RenderContainer"] = 2] = "Element_RenderContainer";
    QUI_ElementType[QUI_ElementType["Element_Image"] = 3] = "Element_Image";
    QUI_ElementType[QUI_ElementType["Element_Image_Scale9"] = 4] = "Element_Image_Scale9";
    QUI_ElementType[QUI_ElementType["Element_Label"] = 5] = "Element_Label";
    QUI_ElementType[QUI_ElementType["Element_Button"] = 6] = "Element_Button";
    QUI_ElementType[QUI_ElementType["Element_Joystick"] = 7] = "Element_Joystick";
    QUI_ElementType[QUI_ElementType["Element_TextBox_Prompt"] = 8] = "Element_TextBox_Prompt";
    //扩展控件
    QUI_ElementType[QUI_ElementType["Element_DragButton"] = 9] = "Element_DragButton";
    QUI_ElementType[QUI_ElementType["Element_Overlay"] = 10] = "Element_Overlay";
    QUI_ElementType[QUI_ElementType["Element_Toggle"] = 11] = "Element_Toggle";
    QUI_ElementType[QUI_ElementType["Element_Bar"] = 12] = "Element_Bar";
    QUI_ElementType[QUI_ElementType["Element_ScrollBar"] = 13] = "Element_ScrollBar";
    //面板
    QUI_ElementType[QUI_ElementType["Element_Panel"] = 14] = "Element_Panel";
    QUI_ElementType[QUI_ElementType["Element_Panel_Scroll"] = 15] = "Element_Panel_Scroll";
    QUI_ElementType[QUI_ElementType["Element_Panel_Split"] = 16] = "Element_Panel_Split";
    QUI_ElementType[QUI_ElementType["Element_Panel_Scroll_Unlimit"] = 17] = "Element_Panel_Scroll_Unlimit";
})(QUI_ElementType || (QUI_ElementType = {}));

(undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};

//Bar 是一种功能很丰富的组件
//主要用来表示进度
var QUI_BarScrollMode;
(function (QUI_BarScrollMode) {
    QUI_BarScrollMode[QUI_BarScrollMode["DoNotScroll"] = 0] = "DoNotScroll";
    QUI_BarScrollMode[QUI_BarScrollMode["ScrollByTouchDrag"] = 1] = "ScrollByTouchDrag";
    QUI_BarScrollMode[QUI_BarScrollMode["ScrollByTouchDrag_LoopValue"] = 2] = "ScrollByTouchDrag_LoopValue";
})(QUI_BarScrollMode || (QUI_BarScrollMode = {}));

//场景封装
var QFrame_ScreenMode;
(function (QFrame_ScreenMode) {
    QFrame_ScreenMode[QFrame_ScreenMode["SameWithWindow"] = 0] = "SameWithWindow";
    QFrame_ScreenMode[QFrame_ScreenMode["LimitRadioFixPixel"] = 1] = "LimitRadioFixPixel";
    QFrame_ScreenMode[QFrame_ScreenMode["LimitRadioScalePixel"] = 2] = "LimitRadioScalePixel";
})(QFrame_ScreenMode || (QFrame_ScreenMode = {}));

class QAni2_Player {
    constructor() {
        this.timer = 0;
    }
    SetFrame(frame) {
        if (frame < 0) {
            frame = 0;
        }
        if (frame >= this.activeani.ani.framecount) {
            frame = this.activeani.ani.framecount - 1;
        }
        this.activeaniframe = frame;
        this.timer = this.activeaniframe / 60;
    }
    SetEndFrame() {
        this.activeaniframe = this.activeani.ani.framecount - 1;
        this.timer = this.activeaniframe / 60;
    }
    Play(name) {
        this.aniname = name;
        this.UpdateAni();
        this.activeaniframe = 0;
        this.end = false;
        this.timer = 0;
    }
    UpdateAni() {
        this.activeani = null;
        let a = this.data.animations[this.aniname];
        if (a != null) {
            this.activeani = a.dirani[this.direction.getDirIndex()];
        }
    }
    Update(delta) {
        if (this.activeani == null) {
            this.UpdateAni();
        }
        if (this.activeani != null) {
            this.timer += delta;
            this.activeaniframe = this.timer * 60 | 0;
            if (this.activeaniframe >= this.activeani.ani.frames.length) {
                if (this.activeani.ani.loop) {
                    this.activeaniframe = 0;
                    this.timer = 0;
                }
                else {
                    this.activeaniframe = this.activeani.ani.frames.length - 1;
                    this.end = true;
                }
            }
        }
    }
    RenderCenter(batcher, color) {
        if (QAni2_Player._whiteS == undefined) {
            QAni2_Player._whiteS = new tt.Sprite(tt.graphic.getWhiteTexture(), null);
        }
        QAni2_Player._whiteS.RenderRect2(batcher, this.posX - 100, this.posY - 1, this.posX + 100, this.posY + 1, color);
        QAni2_Player._whiteS.RenderRect2(batcher, this.posX - 1, this.posY - 100, this.posX + 1, this.posY + 100, color);
    }
    RenderRect(batcher, x1, y1, x2, y2, color) {
        if (QAni2_Player._whiteS == undefined) {
            QAni2_Player._whiteS = new tt.Sprite(tt.graphic.getWhiteTexture(), null);
        }
        let _x1 = this.posX + x1 * QAni2_Player._scale.X;
        let _x2 = this.posX + x2 * QAni2_Player._scale.X;
        let _y1 = this.posY + y1 * QAni2_Player._scale.Y;
        let _y2 = this.posY + y2 * QAni2_Player._scale.Y;
        QAni2_Player._whiteS.RenderRect2(batcher, _x1, _y1, _x2, _y2, color);
    }
    RenderRectBorder(batcher, x1, y1, x2, y2, color) {
        if (QAni2_Player._whiteS == undefined) {
            QAni2_Player._whiteS = new tt.Sprite(tt.graphic.getWhiteTexture(), null);
        }
        let __x1 = this.posX + x1 * QAni2_Player._scale.X;
        let __x2 = this.posX + x2 * QAni2_Player._scale.X;
        let __y1 = this.posY + y1 * QAni2_Player._scale.Y;
        let __y2 = this.posY + y2 * QAni2_Player._scale.Y;
        let _x1 = Math.min(__x1, __x2);
        let _x2 = Math.max(__x1, __x2);
        let _y1 = Math.min(__y1, __y2);
        let _y2 = Math.max(__y1, __y2);
        QAni2_Player._whiteS.RenderRect2(batcher, _x1, _y1, _x2, _y1 + 2, color);
        QAni2_Player._whiteS.RenderRect2(batcher, _x1, _y2 - 2, _x2, _y2, color);
        QAni2_Player._whiteS.RenderRect2(batcher, _x1, _y1, _x1 + 2, _y2, color);
        QAni2_Player._whiteS.RenderRect2(batcher, _x2 - 2, _y1, _x2, _y2, color);
    }
    Render(batcher) {
        let fx = this.activeani.flipx;
        let fy = this.activeani.flipy;
        let f = this.activeani.ani.frames[this.activeaniframe];
        if (this.showrect) {
            QAni2_Player._scale.X = this.scaleX * (fx ? -1 : 1);
            QAni2_Player._scale.Y = this.scaleY * (fy ? -1 : 1);
            for (var i = 0; i < f.rects.length; i++) {
                let r = f.rects[i];
                let c = new tt.Color(255, 255, 255, 100);
                if (r.type == HitType.Attack) {
                    c.R = 1;
                    c.G = c.B = 0;
                }
                else if (r.type == HitType.BeHit) {
                    c.G = 1;
                    c.R = c.B = 0;
                }
                this.RenderRect(batcher, r.x1, r.y1, r.x2, r.y2, c);
            }
        }
        for (var i = 0; i < f.sprites.length; i++) {
            let s = f.sprites[i];
            QAni2_Player._scale.X = this.scaleX * s.ScaleX * (fx ? -1 : 1);
            QAni2_Player._scale.Y = this.scaleY * s.ScaleY * (fy ? -1 : 1);
            QAni2_Player._pos.X = this.posX + s.OffsetX * QAni2_Player._scale.X;
            QAni2_Player._pos.Y = this.posY + s.OffsetY * QAni2_Player._scale.Y;
            f.sprites[i].sprite.Render(batcher, QAni2_Player._pos, QAni2_Player._scale);
        }
        if (this.showrect) {
            QAni2_Player._scale.X = this.scaleX * (fx ? -1 : 1);
            QAni2_Player._scale.Y = this.scaleY * (fy ? -1 : 1);
            for (var i = 0; i < f.rects.length; i++) {
                let r = f.rects[i];
                let c = new tt.Color(255, 255, 255, 100);
                if (r.type == HitType.Attack) {
                    c.R = 1;
                    c.G = c.B = 0;
                }
                else if (r.type == HitType.BeHit) {
                    c.G = 1;
                    c.R = c.B = 0;
                }
                this.RenderRectBorder(batcher, r.x1, r.y1, r.x2, r.y2, c);
            }
        }
    }
}
QAni2_Player._pos = new tt.Vector2(0, 0);
QAni2_Player._scale = new tt.Vector2(1, 1);

var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
//表示动画中引用的具体一块图片
class QAni2_Sprite {
}
//表示动画中的一个判定框
var HitType;
(function (HitType) {
    HitType[HitType["Attack"] = 0] = "Attack";
    HitType[HitType["BeHit"] = 1] = "BeHit";
    HitType[HitType["Other"] = 2] = "Other";
})(HitType || (HitType = {}));
class QAni2_Rect {
}
//表示动画的一帧
class QAni2_Frame {
}
//表示动画的某一个方向
class QAni2_FrameAniData {
}
//表示一个动画
class QAni2_DirAniData {
}
class QAni2_PlayerAniData {
}
class QAni2_Direction {
    constructor(dircount, initdir) {
        this.dircount = dircount;
        let angel = 360 / dircount;
        this.dirindex = (initdir / angel) | 0;
    }
    getDirIndex() {
        return this.dirindex;
    }
    getDirCount() {
        return this.dircount;
    }
    getDirAngle() {
        if (this.dircount == 1)
            return 0;
        if (this.dircount == 0) {
            if (this.dirindex == 0)
                return 90;
            else
                return 270;
        }
        let angel = 360 / this.dircount;
        return this.dirindex * angel;
    }
    getDirName() {
        if (this.dircount == 2) {
            if (this.dirindex == 0)
                return "right";
            else
                return "left";
        }
        else if (this.dircount == 4) {
            if (this.dirindex == 0)
                return "top";
            else if (this.dirindex == 1)
                return "right";
            else if (this.dirindex == 2)
                return "bottom";
            else
                return "left";
        }
        else {
            throw new Error("error dir name");
        }
    }
    setDirCount(dircount) {
        this.dircount = dircount;
    }
    setDirIndex(dirindex) {
        this.dirindex = dirindex;
        if (this.dirindex >= this.dircount)
            this.dirindex = 0;
    }
    setDirValue(v) {
        let angel = 360 / this.dircount;
        this.dirindex = (v / angel) | 0;
    }
    setDirName(dirname) {
        if (this.dircount == 2) {
            if (dirname == "right")
                this.dirindex = 0;
            else
                this.dirindex = 1;
        }
        else if (this.dircount == 4) {
            if (dirname == "top")
                this.dirindex = 0;
            else if (dirname == "right")
                this.dirindex = 1;
            else if (dirname == "down")
                this.dirindex = 2;
            else
                this.dirindex = 3;
        }
        else {
            throw new Error("error dir name");
        }
    }
}
class QAni2_PlayerMgr {
    static FindAni(name, mapani) {
        let aname = name;
        let flipx = false;
        let flipy = false;
        let i = name.indexOf(":");
        if (i >= 0) {
            aname = name.substring(i + 1);
            if (name.includes("flipx:"))
                flipx = true;
            else if (name.includes("flipy:")) {
                flipy = true;
            }
            else if (name.includes("flipxy:")) {
                flipx = flipy = true;
            }
        }
        let ani = mapani[aname];
        if (ani == undefined)
            throw new Error("not find ani:" + name);
        return { srcname: aname, ani: ani, flipx: flipx, flipy: flipy };
    }
    static LoadPlayerInfo(url, ignoreCase = true) {
        return __awaiter$2(this, void 0, void 0, function* () {
            let _url = QFrame_ResMgr.GetDefResPath() + url;
            let jsonstr = yield tt.loader.LoadStringAsync(_url);
            let json = null;
            try {
                json = JSON.parse(jsonstr);
            }
            catch (e) {
                throw new Error(".ttani.json 解析错误");
            }
            console.log("load qani2 json");
            let atlasname = json["atlas"];
            let atlaspath = tt.PathTool.GetPathName(_url);
            let it = _url.lastIndexOf("/");
            if (it >= 0)
                atlaspath = atlaspath + "/" + atlasname;
            else
                atlaspath = atlasname;
            if (atlasname == undefined) {
                throw new Error("atlas name is null");
            }
            console.log("qani2 atlas path=" + atlaspath);
            let atlas = yield QFrame_ResMgr.LoadAtlasAsync(atlaspath, ignoreCase);
            //create Anilist
            let anilist = json.clips;
            if (anilist == undefined) {
                //可能是qani 1.0 的格式，试一下
                return QAni2_PlayerMgr.LoadOldFormat(json, atlas, ignoreCase);
            }
            let mapani = {};
            for (var i = 0; i < anilist.length; i++) {
                let name = anilist[i].name;
                if (ignoreCase)
                    name = name.toLowerCase();
                let frameanidta = new QAni2_FrameAniData();
                mapani[name] = frameanidta;
                frameanidta.framecount = anilist[i].framecount;
                frameanidta.loop = anilist[i].loop;
                let map_frames = {};
                for (var fi = 0; fi < anilist[i].frames.length; fi++) {
                    let framedata = anilist[i].frames[fi];
                    map_frames[framedata.frameid] = framedata;
                }
                frameanidta.frames = [];
                let framecount = anilist[i].framecount;
                for (var fi = 0; fi < framecount; fi++) {
                    let framedata = map_frames[fi];
                    if (framedata != undefined) {
                        let f = new QAni2_Frame();
                        f.sprites = [];
                        if (framedata.sprites == undefined) {
                            throw new Error("framedata.sprites is undefined");
                        }
                        for (var si = 0; si < framedata.sprites.length; si++) {
                            let sprite = framedata.sprites[si];
                            let s = new QAni2_Sprite();
                            let sname = sprite.sprite;
                            if (ignoreCase)
                                sname = sname.toLowerCase();
                            s.sprite = atlas.GetSprite(sname);
                            s.srcname = sname;
                            s.OffsetX = sprite.offsetX;
                            s.OffsetY = sprite.offsetY;
                            if (sprite.offsetX == undefined) {
                                throw new Error("sprite.offsetX is undefined");
                            }
                            if (sprite.offsetY == undefined) {
                                throw new Error("sprite.offsetX is undefined");
                            }
                            if (sprite.scaleX != undefined)
                                s.ScaleX = sprite.scaleX;
                            else
                                s.ScaleX = 1;
                            if (sprite.scaleY != undefined)
                                s.ScaleY = sprite.scaleY;
                            else
                                s.ScaleY = 1;
                            f.sprites.push(s);
                        }
                        f.rects = [];
                        if (framedata.rects != undefined)
                            for (var ri = 0; ri < framedata.rects.length; ri++) {
                                let rect = framedata.rects[ri];
                                let r = new QAni2_Rect();
                                r.x1 = rect.x1;
                                r.y1 = rect.y1;
                                r.x2 = rect.x2;
                                r.y2 = rect.y2;
                                let hittype = rect.type.toLowerCase();
                                if (hittype == "attack")
                                    r.type = HitType.Attack;
                                else if (hittype == "behit")
                                    r.type = HitType.BeHit;
                                else if (hittype == "other")
                                    r.type = HitType.Other;
                                else
                                    throw new Error("Unknown hit type: " + rect.type);
                                r.id = rect.id;
                                f.rects.push(r);
                            }
                        frameanidta.frames.push(f);
                    }
                    else {
                        //reuse last
                        frameanidta.frames.push(frameanidta.frames[frameanidta.frames.length - 1]);
                    }
                }
            }
            //create playermap
            //let playerlist = json.players as any[];
            //for (var i = 0; i < playerlist.length; i++) {
            let name = json.playername;
            if (name == undefined) {
                throw new Error("playername is undefined");
            }
            if (ignoreCase)
                name = name.toLowerCase();
            let srcplayerdata = new QAni2_PlayerAniData();
            this.map_srcplayerdata[name] = srcplayerdata;
            srcplayerdata.playername = name;
            let anis = json.playeranis;
            if (anis == undefined) {
                throw new Error("playeranis is undefined");
            }
            srcplayerdata.animations = {};
            for (var j = 0; j < anis.length; j++) {
                let a = new QAni2_DirAniData();
                let aname = anis[j].aniname;
                if (ignoreCase) {
                    aname = aname.toLowerCase();
                }
                srcplayerdata.animations[aname] = a;
                if (srcplayerdata.defani == null)
                    srcplayerdata.defani = aname;
                let dircount = anis[j].dircount;
                if (dircount == undefined) {
                    throw new Error("dircount is undefined");
                }
                a.dircount = dircount;
                a.dirani = [];
                //一方向动画
                if (dircount == 1) {
                    let rightani = anis[j].right;
                    if (rightani == null)
                        rightani = anis[j].d90;
                    if (rightani == null)
                        rightani = anis[j].ani;
                    a.dirani.push(QAni2_PlayerMgr.FindAni(rightani, mapani));
                    a.framecount = a.dirani[0].ani.framecount;
                    a.loop = a.dirani[0].ani.loop;
                }
                //二方向动画
                if (dircount == 2) {
                    let leftani = anis[j].left;
                    let rightani = anis[j].right;
                    if (leftani == null)
                        leftani = anis[j].d270;
                    if (rightani == null)
                        rightani = anis[j].d90;
                    a.dirani.push(QAni2_PlayerMgr.FindAni(rightani, mapani));
                    a.dirani.push(QAni2_PlayerMgr.FindAni(leftani, mapani));
                    a.framecount = a.dirani[0].ani.framecount;
                    a.loop = a.dirani[0].ani.loop;
                    if (a.framecount != a.dirani[1].ani.framecount)
                        throw new Error("动画帧数不一致");
                    if (a.loop != a.dirani[1].ani.loop)
                        throw new Error("动画循环不一致");
                }
                //四方向动画
                else if (dircount == 4) {
                    let leftani = anis[j].left;
                    let rightani = anis[j].right;
                    let upani = anis[j].up;
                    let downani = anis[j].down;
                    if (upani == null)
                        upani = anis[j].d0;
                    if (rightani == null)
                        rightani = anis[j].d90;
                    if (downani == null)
                        downani = anis[j].d180;
                    if (leftani == null)
                        leftani = anis[j].d270;
                    a.dirani.push(QAni2_PlayerMgr.FindAni(upani, mapani));
                    a.dirani.push(QAni2_PlayerMgr.FindAni(rightani, mapani));
                    a.dirani.push(QAni2_PlayerMgr.FindAni(downani, mapani));
                    a.dirani.push(QAni2_PlayerMgr.FindAni(leftani, mapani));
                    a.framecount = a.dirani[0].ani.framecount;
                    a.loop = a.dirani[0].ani.loop;
                    if (a.framecount != a.dirani[1].ani.framecount ||
                        a.framecount != a.dirani[2].ani.framecount ||
                        a.framecount != a.dirani[3].ani.framecount)
                        throw new Error("动画帧数不一致");
                    if (a.loop != a.dirani[1].ani.loop ||
                        a.loop != a.dirani[2].ani.loop ||
                        a.loop != a.dirani[3].ani.loop)
                        throw new Error("动画循环不一致");
                }
                //八方向，16方向也都可以搞
            }
            return srcplayerdata;
            //}
        });
    }
    static LoadOldFormat(json, atlas, ignoreCase) {
        let mapani = {};
        let anilist = json.animations;
        for (var i = 0; i < anilist.length; i++) {
            let name = anilist[i].name;
            if (ignoreCase)
                name = name.toLowerCase();
            let frameanidta = new QAni2_FrameAniData();
            mapani[name] = frameanidta;
            frameanidta.framecount = anilist[i].framecount;
            frameanidta.loop = anilist[i].loop;
            let map_frames = {};
            for (var fi = 0; fi < anilist[i].frames.length; fi++) {
                let framedata = anilist[i].frames[fi];
                map_frames[framedata.frameid] = framedata;
            }
            frameanidta.frames = [];
            let framecount = anilist[i].framecount;
            for (var fi = 0; fi < framecount; fi++) {
                let framedata = map_frames[fi];
                if (framedata != undefined) {
                    let f = new QAni2_Frame();
                    f.sprites = [];
                    //old format only have 1 sprite
                    //if (framedata.sprites == undefined) {
                    //    throw new Error("framedata.sprites is undefined");
                    //}
                    //for (var si = 0; si < framedata.sprites.length; si++) 
                    {
                        let sprite = framedata;
                        let s = new QAni2_Sprite();
                        let sname = sprite.sprite;
                        if (ignoreCase)
                            sname = sname.toLowerCase();
                        s.sprite = atlas.GetSprite(sname);
                        s.srcname = sname;
                        s.OffsetX = sprite.offsetX;
                        s.OffsetY = sprite.offsetY;
                        if (sprite.offsetX == undefined) {
                            throw new Error("sprite.offsetX is undefined");
                        }
                        if (sprite.offsetY == undefined) {
                            throw new Error("sprite.offsetX is undefined");
                        }
                        if (sprite.scaleX != undefined)
                            s.ScaleX = sprite.scaleX;
                        else
                            s.ScaleX = 1;
                        if (sprite.scaleY != undefined)
                            s.ScaleY = sprite.scaleY;
                        else
                            s.ScaleY = 1;
                        f.sprites.push(s);
                    }
                    f.rects = [];
                    //old format do not have rect
                    // if (framedata.rects != undefined)
                    //     for (var ri = 0; ri < framedata.rects.length; ri++) {
                    //         let rect = framedata.rects[ri];
                    //         let r = new QAni2_Rect();
                    //         r.x1 = rect.x1;
                    //         r.y1 = rect.y1;
                    //         r.x2 = rect.x2
                    //         r.y2 = rect.y2;
                    //         let hittype = (rect.type as string).toLowerCase();
                    //         if (hittype == "attack")
                    //             r.type = HitType.Attack
                    //         else if (hittype == "behit")
                    //             r.type = HitType.BeHit
                    //         else if (hittype == "other")
                    //             r.type = HitType.Other
                    //         else
                    //             throw new Error("Unknown hit type: " + rect.type);
                    //         r.id = rect.id;
                    //         f.rects.push(r);
                    //     }
                    frameanidta.frames.push(f);
                }
                else {
                    //reuse last
                    frameanidta.frames.push(frameanidta.frames[frameanidta.frames.length - 1]);
                }
            }
        }
        //create playermap
        let playerroot = json;
        if (json.players != undefined) {
            let playerlist = json.players;
            playerroot = playerlist[0];
        }
        //let playerlist = json.players as any[];
        //for (var i = 0; i < playerlist.length; i++) {
        let name = playerroot.name;
        if (name == undefined) {
            throw new Error("playername is undefined");
        }
        if (ignoreCase)
            name = name.toLowerCase();
        let srcplayerdata = new QAni2_PlayerAniData();
        this.map_srcplayerdata[name] = srcplayerdata;
        srcplayerdata.playername = name;
        let anis = playerroot.anis;
        if (anis == undefined) {
            throw new Error("playeranis is undefined");
        }
        srcplayerdata.animations = {};
        for (var j = 0; j < anis.length; j++) {
            let a = new QAni2_DirAniData();
            let aname = anis[j].aniname;
            if (ignoreCase) {
                aname = aname.toLowerCase();
            }
            srcplayerdata.animations[aname] = a;
            if (srcplayerdata.defani == null)
                srcplayerdata.defani = aname;
            // let dircount = anis[j].dircount as number;
            // if (dircount == undefined) {
            //     throw new Error("dircount is undefined");
            // }
            a.dircount = 4; //固定四向
            a.dirani = [];
            {
                let leftani = anis[j].left;
                let rightani = anis[j].right;
                let upani = anis[j].up;
                let downani = anis[j].down;
                // if (upani == null)
                //     upani = anis[j].d0 as string;
                // if (rightani == null)
                //     rightani = anis[j].d90 as string;
                // if (downani == null)
                //     downani = anis[j].d180 as string;
                // if (leftani == null)
                //     leftani = anis[j].d270 as string;
                a.dirani.push(QAni2_PlayerMgr.FindAni(upani, mapani));
                a.dirani.push(QAni2_PlayerMgr.FindAni(rightani, mapani));
                a.dirani.push(QAni2_PlayerMgr.FindAni(downani, mapani));
                a.dirani.push(QAni2_PlayerMgr.FindAni(leftani, mapani));
                a.framecount = a.dirani[0].ani.framecount;
                a.loop = a.dirani[0].ani.loop;
                if (a.framecount != a.dirani[1].ani.framecount ||
                    a.framecount != a.dirani[2].ani.framecount ||
                    a.framecount != a.dirani[3].ani.framecount)
                    throw new Error("动画帧数不一致");
                if (a.loop != a.dirani[1].ani.loop ||
                    a.loop != a.dirani[2].ani.loop ||
                    a.loop != a.dirani[3].ani.loop)
                    throw new Error("动画循环不一致");
            }
        }
        return srcplayerdata;
        //}
    }
    static CreatePlayer(name) {
        let sp = this.map_srcplayerdata[name];
        if (sp == undefined)
            return null;
        let p = new QAni2_Player();
        p.posX = 0;
        p.posY = 0;
        p.scaleX = p.scaleY = 1;
        p.direction = new QAni2_Direction(sp.animations[sp.defani].dircount, 0);
        p.data = sp;
        p.aniname = sp.defani;
        //p.activeani = GetAniByDirection(anis, p.direction);
        p.activeaniframe = 0;
        p.Update(0);
        return p;
    }
}
QAni2_PlayerMgr.map_srcplayerdata = {};

//为了开发工具方便，做一些扩展，这些扩展对普通浏览器无用，仅用于
//https://gitee.com/lightsever/ttapi_win
//自制的工具壳
(undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};

var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
function Tool_AniPreviewerInit() {
    return __awaiter$1(this, void 0, void 0, function* () {
        __initDom();
        let impl = new tt_impl.ttimpl_browser();
        impl.Init(_canvas.getHtml());
        //换掉ttloader 这里的加载器和正式环境不同
        tt.loader = new EditorLoader();
        tt.graphic.OnRender = () => {
            if (player != null) {
                batcher.BeginDraw(tt.graphic.getMainScreen());
                player.RenderCenter(batcher, tt.Color.White);
                player.Render(batcher);
                batcher.EndDraw();
            }
        };
        tt.graphic.OnUpdate = (delta) => {
            if (player != null) {
                player.Update(_play ? delta : 0);
            }
        };
        tt.graphic.getMainScreen().ClearColor = new tt.Color(0, 0, 0, 1);
        batcher = tt.graphic.CreateRenderer_Batcher();
        yield __InitPlayer();
    });
}
//由于编辑器环境的加载器和web环境不同，要替换掉
class EditorLoader {
    LoadStringAsync(name) {
        return __awaiter$1(this, void 0, void 0, function* () {
            return yield IO.LoadTextFile(name);
        });
    }
    LoadBinaryAsync(name) {
        return __awaiter$1(this, void 0, void 0, function* () {
            return yield IO.LoadBinaryFile(name);
        });
    }
    LoadImageAsync(name) {
        return __awaiter$1(this, void 0, void 0, function* () {
            let bin = yield this.LoadBinaryAsync(name);
            return yield ImgTool.Bin2ImgAsync(new Uint8Array(bin));
        });
    }
    LoadImageDataAsync(name, gray) {
        return __awaiter$1(this, void 0, void 0, function* () {
            let bin = yield this.LoadBinaryAsync(name);
            let data = yield ImgTool.Bin2ImgDataAsync(new Uint8Array(bin));
            let buf = new tt.ImageBuffer();
            buf.gray = false;
            buf.data = data.data;
            buf.width = data.width;
            buf.height = data.height;
            return buf;
        });
    }
}
var _canvas;
var _debugpanel;
var _anipanel;
var _infopanel;
var _dirpanel;
var _framepanel;
function __initDom() {
    DomTool.InitFullScreen();
    let sall = DomTool.AddSpliter();
    sall.Style_Fill();
    sall.SetSplitPosBottom(300);
    let s1 = new Splitter();
    s1.Style_Fill();
    s1.SetSplitPosLeft(200);
    sall._panel1.AddChild(s1);
    //底栏
    let sbot = new Splitter();
    sbot.Style_Fill();
    sbot.SetSplitPosRight(400);
    sall._panel2.AddChild(sbot);
    //左竖边栏
    let s2 = new Splitter();
    s2.SetSplitPosBottom(200);
    s1._panel1.AddChild(s2);
    s2.Style_Fill();
    {
        let sa = new Splitter();
        sa.SetSplitPosTop(24);
        s2._panel1.AddChild(sa);
        let toolbox = new Group();
        toolbox.SetTitle("动画");
        _anipanel = toolbox._panel;
        _anipanel.UseScrollV();
        //toolbox._root.style.backgroundColor = "#33c";
        sa._panel2.AddChild(toolbox);
        let btn = new Button("重加载");
        btn.SetClickEvent(() => __awaiter$1(this, void 0, void 0, function* () {
            __InitPlayer();
        }));
        new Button("保存");
        sa._panel1.AddChild(btn);
        //sa._panel1.AddChild(btn2);
    }
    {
        let toolbox = new Group();
        toolbox.SetTitle("方向");
        _dirpanel = toolbox._panel;
        //toolbox._root.style.backgroundColor = "#33c";
        s2._panel2.AddChild(toolbox);
    }
    let sr = DomTool.AddSpliter();
    sr.SetSplitPosRight(200);
    s1._panel2.AddChild(sr);
    //中栏
    let sc = new Splitter();
    sc.SetSplitPosBottom(24);
    sr._panel1.AddChild(sc);
    sc.Style_Fill();
    {
        let toolbox = new Group();
        toolbox.SetTitle("预览");
        //toolbox._root.style.backgroundColor = "#33c";
        sc._panel1.AddChild(toolbox);
        let c = _canvas = new CanvasRaw();
        toolbox._panel.AddChild(c);
        c.Style_Fill();
        c.getRoot().style.backgroundColor = "#000";
        let btn = new Button("Play", () => {
            _play = true;
            _pickframe = -1;
            SetFrameUI();
        });
        sc._panel2.AddChild(btn);
        let btn2 = new Button("Stop", () => {
            _play = false;
        });
        sc._panel2.AddChild(btn2);
        let btn3 = new Button("|<", () => {
            _play = false;
            if (player != null) {
                SelectFrame(0);
            }
        });
        sc._panel2.AddChild(btn3);
        let btn4 = new Button("<", () => {
            _play = false;
            if (player != null) {
                SelectFrame(player.activeaniframe - 1);
            }
        });
        sc._panel2.AddChild(btn4);
        let btn5 = new Button(">", () => {
            _play = false;
            if (player != null) {
                SelectFrame(player.activeaniframe + 1);
            }
        });
        sc._panel2.AddChild(btn5);
        let btn6 = new Button(">|", () => {
            _play = false;
            if (player != null) {
                SelectFrame(Number.MAX_SAFE_INTEGER);
            }
        });
        sc._panel2.AddChild(btn6);
        let l = new Space();
        sc._panel2.AddChild(l);
        let zbtn1 = new Button("X1", () => {
            batcher.Scale = 1.0;
        });
        sc._panel2.AddChild(zbtn1);
        let zbtn2 = new Button("X2", () => {
            batcher.Scale = 2.0;
        });
        sc._panel2.AddChild(zbtn2);
        let zbtn4 = new Button("X4", () => {
            batcher.Scale = 4.0;
        });
        sc._panel2.AddChild(zbtn4);
    }
    // //右边栏
    // let s3 = new DOM.Splitter();
    // s3.SetSplitPosTop(600);
    // sr._panel2.AddChild(s3);
    // s3.Style_Fill();
    {
        let toolbox = new Group();
        toolbox.SetTitle("属性");
        _infopanel = toolbox._panel;
        _infopanel.UseScrollV();
        //toolbox._root.style.backgroundColor = "#33c";
        sr._panel2.AddChild(toolbox);
    }
    //底栏
    {
        let toolbox = new Group();
        toolbox.SetTitle("帧");
        //toolbox._root.style.backgroundColor = "#33c";
        _framepanel = toolbox._panel;
        _framepanel.UseScrollH();
        sbot._panel1.AddChild(toolbox);
    }
    {
        let toolbox = new Group();
        _debugpanel = toolbox._panel;
        toolbox.SetTitle("信息");
        //toolbox._root.style.backgroundColor = "#33c";
        sbot._panel2.AddChild(toolbox);
    }
}
function Log(txt) {
    console.warn(txt);
    _debugpanel.AddChild(new Label(txt));
}
var batcher;
var player;
var _play = true;
function __InitPlayer() {
    return __awaiter$1(this, void 0, void 0, function* () {
        _debugpanel.RemoveAllChild();
        _anipanel.RemoveAllChild();
        let file = IO.GetEditFileName();
        try {
            let p = yield QAni2_PlayerMgr.LoadPlayerInfo(file, true);
            Log("load:" + p.playername);
            player = QAni2_PlayerMgr.CreatePlayer(p.playername);
            if (_pickani == undefined) {
                _pickani = player.aniname;
                _pickdir = player.direction.getDirIndex();
            }
            else {
                player.direction.setDirIndex(_pickdir);
                player.Play(_pickani);
                player.Update(0);
            }
            player.showrect = true;
            SetAniUI();
            SetDirUI();
            SetFrameUI();
            pickAni(_pickani, 0);
        }
        catch (e) {
            Log(e.toString());
            throw e;
        }
    });
}
function SetAniUI() {
    let pickButton = null;
    //for (var i = 0; i < 50; i++) 
    {
        for (var key in player.data.animations) {
            let btn = new LabelButton(key);
            _anipanel.AddChild(btn);
            let aname = key;
            if (aname == _pickani) {
                btn.colorBack = new Color32(128, 128, 0, 255);
                btn.colorLabel = new Color32(0, 0, 128, 255);
                btn.UpdateColor();
                pickButton = btn;
            }
            btn.onClick = () => {
                if (pickButton == btn) {
                    return;
                }
                if (pickButton != null) {
                    pickButton.colorBack = new Color32(0, 0, 0, 0);
                    pickButton.colorLabel = new Color32(0, 0, 0, 255);
                    pickButton.UpdateColor();
                }
                pickButton = btn;
                pickButton.colorBack = new Color32(128, 128, 0, 255);
                pickButton.colorLabel = new Color32(0, 0, 128, 255);
                pickButton.UpdateColor();
                pickAni(aname, _pickdir);
            };
        }
    }
}
function SetDirUI() {
    let pickButton = null;
    if (_dirpanel.GetChildCount() != player.direction.getDirCount()) {
        _dirpanel.RemoveAllChild();
        for (let i = 0; i < player.direction.getDirCount(); i++) {
            let dir = new QAni2_Direction(player.direction.getDirCount(), i);
            dir.setDirIndex(i);
            let dname = dir.getDirName();
            let btn = new LabelButton(dname);
            _dirpanel.AddChild(btn);
            if (i == _pickdir) {
                btn.colorBack = new Color32(128, 128, 0, 255);
                btn.colorLabel = new Color32(0, 0, 128, 255);
                btn.UpdateColor();
                pickButton = btn;
            }
            btn.onClick = () => {
                if (pickButton == btn) {
                    return;
                }
                if (pickButton != null) {
                    pickButton.colorBack = new Color32(0, 0, 0, 0);
                    pickButton.colorLabel = new Color32(0, 0, 0, 255);
                    pickButton.UpdateColor();
                }
                pickButton = btn;
                pickAni(_pickani, dir.getDirIndex());
                //player.direction.setDirIndex(dir.getDirIndex());    
                pickButton.colorBack = new Color32(128, 128, 0, 255);
                pickButton.colorLabel = new Color32(0, 0, 128, 255);
                pickButton.UpdateColor();
            };
        }
    }
}
function SelectFrame(frame) {
    if (frame < 0)
        frame = 0;
    if (frame > player.activeani.ani.framecount)
        frame = player.activeani.ani.framecount - 1;
    if (_pickframe >= 0) {
        let b = _framepanel.GetChild(_pickframe);
        b.colorBack = new Color32(0, 0, 0, 0);
        b.UpdateColor();
    }
    _pickframe = frame;
    if (player != null)
        player.SetFrame(_pickframe);
    _play = false;
    let b = _framepanel.GetChild(_pickframe);
    b.colorBack = new Color32(128, 128, 0);
    b.UpdateColor();
    SetInfoUI();
}
function SetFrameUI() {
    _framepanel.RemoveAllChild();
    _framepanel._root.style.display = "block";
    _framepanel._root.style.overflowX = "scroll";
    _framepanel._root.style.overflowY = "hidden";
    _framepanel._root.style.whiteSpace = "nowrap";
    for (var i = 0; i < player.activeani.ani.frames.length; i++) {
        let f = player.activeani.ani.frames[i];
        let key = true;
        if (i > 0) {
            let flast = player.activeani.ani.frames[i - 1];
            if (f == flast) {
                key = false;
            }
        }
        let l = new LabelButton();
        l._root.style.width = "32px";
        l._root.style.height = "100%";
        l._root.style.display = "inline-block";
        l._root.style.position = "relative";
        l._root.style.border = "1px solid #000";
        l._root.style.verticalAlign = "top";
        l._root.style.textAlign = "center";
        let _frame = i;
        l.onClick = () => {
            SelectFrame(_frame);
        };
        _framepanel.AddChild(l);
        let lid = new Label(i.toString());
        l.AddChild(lid);
        let lkey = new Label(key ? "K" : " ");
        lkey._root.style.border = "1px solid #000";
        lkey._root.style.backgroundColor = " #ddd";
        lkey._root.style.color = "000";
        lkey._root.style.width = "30px";
        lkey._root.style.height = "24px";
        l.AddChild(lkey);
        if (key) {
            //精灵标记
            for (var is = 0; is < f.sprites.length; is++) {
                let ls = new Label("S");
                ls._root.style.color = "#ff0";
                ls._root.style.border = "1px solid #ff0";
                ls._root.style.backgroundColor = "#888";
                ls._root.style.width = "30px";
                ls._root.style.height = "24px";
                l.AddChild(ls);
            }
            //Rect标记
            for (var ir = 0; ir < f.rects.length; ir++) {
                let ls = new Label("R");
                let color = "";
                if (f.rects[ir].type == HitType.Attack)
                    color = "#f00";
                if (f.rects[ir].type == HitType.BeHit)
                    color = "#0f0";
                if (f.rects[ir].type == HitType.Other)
                    color = "#00f";
                ls._root.style.color = color;
                ls._root.style.border = "1px solid " + color;
                ls._root.style.backgroundColor = "#000";
                ls._root.style.width = "30px";
                ls._root.style.height = "24px";
                l.AddChild(ls);
            }
        }
    }
}
var _pickani;
var _pickdir = 0;
var _pickframe = -1;
function pickAni(name, dirindex) {
    return __awaiter$1(this, void 0, void 0, function* () {
        _pickani = name;
        _pickdir = dirindex;
        SetDirUI();
        let a = player.data.animations[_pickani];
        player.direction.setDirCount(a.dircount);
        player.direction.setDirIndex(_pickdir);
        player.UpdateAni();
        SetFrameUI();
        SetInfoUI();
    });
}
function SetInfoUI() {
    _infopanel.RemoveAllChild();
    _infopanel.AddChild(new Label("====Pick Animation: " + name));
    let a = player.data.animations[_pickani];
    _infopanel.AddChild(new Label("dircount: " + a.dircount));
    _infopanel.AddChild(new Label("framecount: " + a.framecount));
    _infopanel.AddChild(new Label("loop: " + a.loop));
    _infopanel.AddChild(new Label("====dir: " + player.direction.getDirName()));
    _infopanel.AddChild(new Label("srcname: " + player.activeani.srcname));
    _infopanel.AddChild(new Label("flipx: " + player.activeani.flipx));
    _infopanel.AddChild(new Label("flipy: " + player.activeani.flipy));
    if (_pickframe >= 0) {
        _infopanel.AddChild(new Label("====frame: " + _pickframe));
        let f = a.dirani[_pickdir].ani.frames[_pickframe];
        _infopanel.AddChild(new Label("当前帧精灵数量:" + f.sprites.length));
        for (var i = 0; i < f.sprites.length; i++) {
            let fs = f.sprites[i];
            _infopanel.AddChild(new Label("sprite[" + i + "]: " + fs.srcname));
            _infopanel.AddChild(new Label("  --offset: " + fs.OffsetX + "," + fs.OffsetY));
            _infopanel.AddChild(new Label("  --scale: " + fs.ScaleX + "," + fs.ScaleY));
        }
        _infopanel.AddChild(new Label("当前帧rect数量: " + f.rects.length));
        for (var i = 0; i < f.rects.length; i++) {
            let fr = f.rects[i];
            _infopanel.AddChild(new Label("rect[" + i + "]: " + HitType[fr.type].toString()));
            _infopanel.AddChild(new Label("  --: (" + fr.x1 + "," + fr.y1 + " - " + fr.x2 + "," + fr.y2 + ")"));
        }
    }
}

var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
window.onload = () => __awaiter(void 0, void 0, void 0, function* () {
    IO.Init(); //初始化扩展IO系统
    let filename = IO.GetEditFileName();
    console.log("argv:" + filename);
    let ext = TTPathTool.GetExt(filename);
    if (ext == ".ttani.json") {
        console.log("这是一个动画文件");
        Tool_AniPreviewerInit();
    }
    else {
        DomTool.Init();
        DomTool.AddLabel("不支持的文件:" + filename);
        console.log("不支持的文件");
        return;
    }
});
